@salesforce/afv-skills 1.65.0 → 1.65.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.
Files changed (349) hide show
  1. package/package.json +1 -1
  2. package/skills/field-service-data-capture-form-deployer-configure/SKILL.md +2 -23
  3. package/skills/field-service-data-capture-form-designer-configure/SKILL.md +3 -13
  4. package/skills/field-service-mobile-branding-configure/SKILL.md +3 -17
  5. package/skills/field-service-objective-designer-configure/SKILL.md +379 -72
  6. package/skills/field-service-prework-brief-deployer-configure/SKILL.md +537 -10
  7. package/skills/field-service-scheduling-policy-designer-query/SKILL.md +1093 -65
  8. package/skills/field-service-sobject-create-configure/SKILL.md +213 -13
  9. package/skills/field-service-voice-to-form-configure/SKILL.md +11 -40
  10. package/skills/field-service-work-rule-designer-configure/SKILL.md +95 -121
  11. package/skills/agentforce-gpt-email-generation-configure/SKILL.md +0 -312
  12. package/skills/agentforce-wizard-ui-forecasting-analytics-components-configure/SKILL.md +0 -64
  13. package/skills/agentforce-wizard-ui-sales-einstein-components-configure/SKILL.md +0 -46
  14. package/skills/automation-company-profile-info-query/SKILL.md +0 -51
  15. package/skills/automation-digital-wallet-configure/SKILL.md +0 -689
  16. package/skills/automation-flow-coordinate/SKILL.md +0 -95
  17. package/skills/automation-flow-coordinate/references/elements/action-call/action-call.md +0 -1569
  18. package/skills/automation-flow-coordinate/references/elements/action-call/conversation-messaging.md +0 -96
  19. package/skills/automation-flow-coordinate/references/elements/action-call/examples/send-sms-message.json +0 -23
  20. package/skills/automation-flow-coordinate/references/elements/action-call/examples/send-to-journey-no-fields.json +0 -31
  21. package/skills/automation-flow-coordinate/references/elements/action-call/send-to-journey.md +0 -179
  22. package/skills/automation-flow-coordinate/references/elements/exit-rule/examples/event-email-link-click.json +0 -13
  23. package/skills/automation-flow-coordinate/references/elements/exit-rule/exit-rule.md +0 -83
  24. package/skills/automation-flow-coordinate/references/elements/forward-to-bot-or-agent/examples/forward-to-bot-or-agent.json +0 -69
  25. package/skills/automation-flow-coordinate/references/elements/forward-to-bot-or-agent/forward-to-bot-or-agent.md +0 -274
  26. package/skills/automation-flow-coordinate/references/elements/start/audience-common.md +0 -192
  27. package/skills/automation-flow-coordinate/references/elements/start/automation-event-triggered.md +0 -130
  28. package/skills/automation-flow-coordinate/references/elements/start/data-cloud-data-change-triggered.md +0 -92
  29. package/skills/automation-flow-coordinate/references/elements/start/form-submission-triggered.md +0 -88
  30. package/skills/automation-flow-coordinate/references/elements/start/segment-triggered.md +0 -152
  31. package/skills/automation-flow-coordinate/references/elements/start/start.md +0 -539
  32. package/skills/automation-flow-coordinate/references/elements/subflow/subflow.md +0 -152
  33. package/skills/automation-flow-coordinate/references/elements/wait/wait.md +0 -497
  34. package/skills/automation-flow-coordinate/references/recipes/author/author.md +0 -868
  35. package/skills/automation-flow-coordinate/references/recipes/summarize/element-generic.md +0 -300
  36. package/skills/automation-flow-coordinate/references/recipes/summarize/reference-resolution.md +0 -292
  37. package/skills/automation-flow-coordinate/references/recipes/summarize/summarize.md +0 -323
  38. package/skills/automation-flow-coordinate/references/recipes/testing/examples/debug-output-template.md +0 -109
  39. package/skills/automation-flow-coordinate/references/recipes/testing/shape.md +0 -219
  40. package/skills/automation-flow-coordinate/references/recipes/testing/shared-stages/intent-analyzer.md +0 -180
  41. package/skills/automation-flow-coordinate/references/recipes/testing/workflows/debug.md +0 -175
  42. package/skills/automation-flow-coordinate/references/recipes/testing/workflows/promote.md +0 -255
  43. package/skills/automation-flow-coordinate/references/recipes/testing/workflows/save-debug.md +0 -212
  44. package/skills/automation-flow-coordinate/references/resources/variable/variable.md +0 -171
  45. package/skills/automation-flow-coordinate/references/shared/anti-patterns.md +0 -786
  46. package/skills/automation-flow-coordinate/references/shared/end/end.md +0 -267
  47. package/skills/automation-flow-coordinate/references/shared/flow-type-resolver/eval/README.md +0 -69
  48. package/skills/automation-flow-coordinate/references/shared/flow-type-resolver/eval/predictions.jsonl +0 -80
  49. package/skills/automation-flow-coordinate/references/shared/flow-type-resolver/eval/prompts.jsonl +0 -116
  50. package/skills/automation-flow-coordinate/references/shared/flow-type-resolver/flow-type-resolver.md +0 -321
  51. package/skills/automation-flow-coordinate/references/shared/flow-type-resolver/taxonomy.json +0 -320
  52. package/skills/automation-flow-coordinate/references/shared/server-functions.md +0 -597
  53. package/skills/automotive-cloud-appraisal-management-settings-configure/SKILL.md +0 -69
  54. package/skills/automotive-cloud-dealer-essentials-configure/SKILL.md +0 -78
  55. package/skills/automotive-cloud-ground-agent-data-pipelines-configure/SKILL.md +0 -373
  56. package/skills/automotive-cloud-ground-mfg-agent-data-pipelines-configure/SKILL.md +0 -273
  57. package/skills/automotive-cloud-mfg-agent-data-pipelines-configure/SKILL.md +0 -259
  58. package/skills/automotive-cloud-mfg-sales-concierge-agent-create/SKILL.md +0 -369
  59. package/skills/automotive-cloud-mfg-sales-concierge-foundation-configure/SKILL.md +0 -542
  60. package/skills/automotive-cloud-mfg-sales-concierge-foundation-coordinate/SKILL.md +0 -159
  61. package/skills/automotive-cloud-mfg-sales-concierge-orchestrator-agent-create/SKILL.md +0 -349
  62. package/skills/automotive-cloud-mfg-sales-concierge-orchestrator-configure/SKILL.md +0 -505
  63. package/skills/automotive-cloud-sales-concierge-agent-create/SKILL.md +0 -380
  64. package/skills/automotive-cloud-sales-concierge-foundation-configure/SKILL.md +0 -567
  65. package/skills/automotive-cloud-sales-concierge-foundation-coordinate/SKILL.md +0 -159
  66. package/skills/automotive-cloud-sales-concierge-orchestrator-agent-create/SKILL.md +0 -360
  67. package/skills/automotive-cloud-sales-concierge-orchestrator-configure/SKILL.md +0 -539
  68. package/skills/automotive-cloud-sales-concierge-search-grounding-configure/SKILL.md +0 -358
  69. package/skills/commerce-b2b-merchant-quote-configure/SKILL.md +0 -602
  70. package/skills/commerce-b2b-store-configure/SKILL.md +0 -251
  71. package/skills/commerce-credit-memo-create/SKILL.md +0 -139
  72. package/skills/commerce-delivery-business-hours-configure/SKILL.md +0 -277
  73. package/skills/commerce-delivery-estimation-apply/SKILL.md +0 -155
  74. package/skills/commerce-delivery-estimation-configure/SKILL.md +0 -377
  75. package/skills/commerce-delivery-routing-specs-integrate/SKILL.md +0 -174
  76. package/skills/commerce-estimate-bopis-query/SKILL.md +0 -279
  77. package/skills/commerce-estimate-delivery-date-query/SKILL.md +0 -349
  78. package/skills/commerce-fees-apply/SKILL.md +0 -227
  79. package/skills/commerce-fulfillment-routing-analyze/SKILL.md +0 -200
  80. package/skills/commerce-guest-buyer-create/SKILL.md +0 -173
  81. package/skills/commerce-location-bopis-configure/SKILL.md +0 -378
  82. package/skills/commerce-location-business-hours-configure/SKILL.md +0 -235
  83. package/skills/commerce-location-delivery-processing-time-configure/SKILL.md +0 -309
  84. package/skills/commerce-location-graph-integrate/SKILL.md +0 -131
  85. package/skills/commerce-location-shipping-carrier-method-configure/SKILL.md +0 -755
  86. package/skills/commerce-location-temporary-operating-hours-configure/SKILL.md +0 -331
  87. package/skills/commerce-merchant-buyer-groups-configure/SKILL.md +0 -579
  88. package/skills/commerce-merchant-buyer-user-onboarding-create/SKILL.md +0 -626
  89. package/skills/commerce-merchant-catalog-configure/SKILL.md +0 -614
  90. package/skills/commerce-merchant-extension-provider-configure/SKILL.md +0 -368
  91. package/skills/commerce-merchant-insights-account-health-churn-analyze/SKILL.md +0 -118
  92. package/skills/commerce-merchant-insights-inventory-returns-analyze/SKILL.md +0 -65
  93. package/skills/commerce-merchant-insights-order-shopper-get/SKILL.md +0 -90
  94. package/skills/commerce-merchant-insights-product-performance-analyze/SKILL.md +0 -72
  95. package/skills/commerce-merchant-insights-query/SKILL.md +0 -279
  96. package/skills/commerce-merchant-storefront-guest-access-configure/SKILL.md +0 -656
  97. package/skills/commerce-order-summary-change-apply/SKILL.md +0 -375
  98. package/skills/commerce-product-details-get/SKILL.md +0 -363
  99. package/skills/commerce-product-search/SKILL.md +0 -563
  100. package/skills/commerce-react-storefront-create/SKILL.md +0 -350
  101. package/skills/commerce-return-order-summary-create/SKILL.md +0 -513
  102. package/skills/commerce-search-relevance-analyze/SKILL.md +0 -757
  103. package/skills/commerce-temporary-processing-time-override-create/SKILL.md +0 -274
  104. package/skills/communications-initial-setup-configure/SKILL.md +0 -630
  105. package/skills/communications-multisite-configure/SKILL.md +0 -571
  106. package/skills/communications-revenue-cloud-settings-configure/SKILL.md +0 -74
  107. package/skills/communications-smb-fiber-catalog-create/SKILL.md +0 -413
  108. package/skills/communications-tmf-integrate/SKILL.md +0 -1368
  109. package/skills/consumer-goods-visit-activity-act4-questions-assign/SKILL.md +0 -448
  110. package/skills/consumer-goods-visit-activity-act5-standard-create/SKILL.md +0 -269
  111. package/skills/consumer-goods-visit-activity-act6-assortment-configure/SKILL.md +0 -271
  112. package/skills/consumer-goods-visit-activity-act7-account-scoping-create/SKILL.md +0 -382
  113. package/skills/consumer-goods-visit-activity-act8-plan-attach-create/SKILL.md +0 -373
  114. package/skills/consumer-goods-visit-activity-act9-proof-validate/SKILL.md +0 -389
  115. package/skills/consumer-goods-visit-and-activity-act1-specification-create/SKILL.md +0 -338
  116. package/skills/consumer-goods-visit-and-activity-act2-template-create/SKILL.md +0 -547
  117. package/skills/consumer-goods-visit-and-activity-act3-definition-create/SKILL.md +0 -346
  118. package/skills/consumer-goods-visit-and-activity-configure/SKILL.md +0 -1110
  119. package/skills/crm-analytics-adoption-insights-configure/SKILL.md +0 -224
  120. package/skills/crm-analytics-custom-report-type-lightning-configure/SKILL.md +0 -418
  121. package/skills/crm-analytics-dashboards-configure/SKILL.md +0 -731
  122. package/skills/crm-analytics-operational-analytics-metadata-bridge-migrate/SKILL.md +0 -233
  123. package/skills/crm-analytics-report-notifications-ui-configure/SKILL.md +0 -184
  124. package/skills/crm-analytics-sharing-inheritance-coverage-analyze/SKILL.md +0 -167
  125. package/skills/crm-analytics-sonic-getting-started-configure/SKILL.md +0 -55
  126. package/skills/crm-analytics-tableau-next-app-deploy/SKILL.md +0 -1350
  127. package/skills/crm-analytics-tableau-next-c360-basemodel-validate/SKILL.md +0 -1862
  128. package/skills/crm-analytics-tableau-next-datacloud-validate/SKILL.md +0 -83
  129. package/skills/crm-analytics-tableau-next-provisioning-validate/SKILL.md +0 -81
  130. package/skills/data360-activation-create/SKILL.md +0 -104
  131. package/skills/data360-activation-external-platform-get/SKILL.md +0 -49
  132. package/skills/data360-activation-target-create/SKILL.md +0 -93
  133. package/skills/data360-activation-v2-create/SKILL.md +0 -456
  134. package/skills/data360-audience-dmo-get/SKILL.md +0 -38
  135. package/skills/data360-calculated-affinity-create/SKILL.md +0 -82
  136. package/skills/data360-calculated-insight-create/SKILL.md +0 -148
  137. package/skills/data360-catalog-metadata-create/SKILL.md +0 -183
  138. package/skills/data360-code-extension-create/SKILL.md +0 -115
  139. package/skills/data360-communication-capping-create/SKILL.md +0 -227
  140. package/skills/data360-connection-create/SKILL.md +0 -491
  141. package/skills/data360-content-lens-create/SKILL.md +0 -181
  142. package/skills/data360-data-action-create/SKILL.md +0 -126
  143. package/skills/data360-data-action-target-create/SKILL.md +0 -181
  144. package/skills/data360-data-clean-room-create/SKILL.md +0 -238
  145. package/skills/data360-data-governance-create/SKILL.md +0 -746
  146. package/skills/data360-data-graph-create/SKILL.md +0 -82
  147. package/skills/data360-data-harmonized-model-object-reference-create/SKILL.md +0 -38
  148. package/skills/data360-data-lake-object-create/SKILL.md +0 -207
  149. package/skills/data360-data-model-object-catalog-get/SKILL.md +0 -49
  150. package/skills/data360-data-model-object-create/SKILL.md +0 -128
  151. package/skills/data360-data-model-object-mapping-create/SKILL.md +0 -106
  152. package/skills/data360-data-space-create/SKILL.md +0 -161
  153. package/skills/data360-data-streams-create/SKILL.md +0 -324
  154. package/skills/data360-engagement-signal-get/SKILL.md +0 -38
  155. package/skills/data360-experiment-create/SKILL.md +0 -82
  156. package/skills/data360-goal-based-campaign-create/SKILL.md +0 -192
  157. package/skills/data360-harmonization-process-history-get/SKILL.md +0 -38
  158. package/skills/data360-harmonized-unstructured-dmo-create/SKILL.md +0 -84
  159. package/skills/data360-identity-resolution-create/SKILL.md +0 -115
  160. package/skills/data360-intelligent-document-processing-create/SKILL.md +0 -148
  161. package/skills/data360-knowledge-graph-construction-create/SKILL.md +0 -93
  162. package/skills/data360-knowledge-graph-create/SKILL.md +0 -216
  163. package/skills/data360-knowledge-space-mcp-configure/SKILL.md +0 -475
  164. package/skills/data360-machine-learning-mcp-configure/SKILL.md +0 -697
  165. package/skills/data360-multi-org-mcp-configure/SKILL.md +0 -117
  166. package/skills/data360-secondary-index-mcp-configure/SKILL.md +0 -126
  167. package/skills/data360-segment-mcp-configure/SKILL.md +0 -159
  168. package/skills/data360-semantic-calculated-dimension-mcp-configure/SKILL.md +0 -93
  169. package/skills/data360-semantic-calculated-measurement-mcp-configure/SKILL.md +0 -93
  170. package/skills/data360-semantic-data-object-mcp-configure/SKILL.md +0 -93
  171. package/skills/data360-semantic-dimension-mcp-configure/SKILL.md +0 -38
  172. package/skills/data360-semantic-engine-mcp-configure/SKILL.md +0 -62
  173. package/skills/data360-semantic-measurement-mcp-configure/SKILL.md +0 -38
  174. package/skills/data360-semantic-metric-mcp-configure/SKILL.md +0 -93
  175. package/skills/data360-semantic-model-mcp-configure/SKILL.md +0 -115
  176. package/skills/data360-standard-mapping-mcp-configure/SKILL.md +0 -38
  177. package/skills/data360-tenant-lifecycle-mcp-configure/SKILL.md +0 -73
  178. package/skills/experience-afv-query/SKILL.md +0 -234
  179. package/skills/experience-agentforce-vibes-in-react-configure/SKILL.md +0 -63
  180. package/skills/experience-apex-components-configure/SKILL.md +0 -69
  181. package/skills/experience-apex-pages-configure/SKILL.md +0 -107
  182. package/skills/experience-app-home-configure/SKILL.md +0 -76
  183. package/skills/experience-aura-lightning-out-access-configure/SKILL.md +0 -58
  184. package/skills/experience-briefcase-configure/SKILL.md +0 -287
  185. package/skills/experience-community-flexi-page-list-create/SKILL.md +0 -74
  186. package/skills/experience-community-template-definition-list-query/SKILL.md +0 -48
  187. package/skills/experience-community-theme-definition-list-query/SKILL.md +0 -38
  188. package/skills/experience-content-assets-configure/SKILL.md +0 -86
  189. package/skills/experience-custom-domain-create/SKILL.md +0 -727
  190. package/skills/experience-defer-rendering-workspace-page-setup-configure/SKILL.md +0 -41
  191. package/skills/experience-field-service-app-builder-configure/SKILL.md +0 -187
  192. package/skills/experience-flow-category-configure/SKILL.md +0 -117
  193. package/skills/experience-formula-configure/SKILL.md +0 -358
  194. package/skills/experience-global-actions-configure/SKILL.md +0 -69
  195. package/skills/experience-global-publisher-layouts-configure/SKILL.md +0 -79
  196. package/skills/experience-instrumentation-configure/SKILL.md +0 -64
  197. package/skills/experience-lex-engagement-configure/SKILL.md +0 -50
  198. package/skills/experience-lightning-bolt-open/SKILL.md +0 -12
  199. package/skills/experience-lightning-out-configure/SKILL.md +0 -129
  200. package/skills/experience-lightning-preview-configure/SKILL.md +0 -67
  201. package/skills/experience-lightning-types-query/SKILL.md +0 -98
  202. package/skills/experience-malware-scan-configure/SKILL.md +0 -71
  203. package/skills/experience-my-salesforce-configure/SKILL.md +0 -126
  204. package/skills/experience-navigation-menus-configure/SKILL.md +0 -235
  205. package/skills/experience-notification-types-configure/SKILL.md +0 -206
  206. package/skills/experience-notifications-configure/SKILL.md +0 -75
  207. package/skills/experience-record-discovery-query/SKILL.md +0 -227
  208. package/skills/experience-regenerate-previews-open/SKILL.md +0 -94
  209. package/skills/experience-sales-cloud-mab-configure/SKILL.md +0 -254
  210. package/skills/experience-schema-builder-open/SKILL.md +0 -38
  211. package/skills/experience-simple-record-home-configure/SKILL.md +0 -110
  212. package/skills/experience-static-resources-configure/SKILL.md +0 -74
  213. package/skills/field-service-setup-coordinate/SKILL.md +0 -62
  214. package/skills/field-service-work-plan-configure/SKILL.md +0 -253
  215. package/skills/fsc-actionable-segmentation-settings-configure/SKILL.md +0 -180
  216. package/skills/fsc-digital-origination-bre-decision-matrix-foundation-create/SKILL.md +0 -196
  217. package/skills/fsc-digital-origination-bre-disclosures-consent-configure/SKILL.md +0 -106
  218. package/skills/fsc-digital-origination-bre-document-upload-configure/SKILL.md +0 -96
  219. package/skills/fsc-digital-origination-bre-permissions-assign/SKILL.md +0 -111
  220. package/skills/fsc-digital-origination-bre-setup-coordinate/SKILL.md +0 -250
  221. package/skills/fsc-digital-origination-bre-stp-eligibility-configure/SKILL.md +0 -122
  222. package/skills/fsc-digital-origination-coordinate/SKILL.md +0 -653
  223. package/skills/fsc-digital-origination-demo-run/SKILL.md +0 -225
  224. package/skills/fsc-digital-origination-execution-guardrails-validate/SKILL.md +0 -112
  225. package/skills/fsc-digital-origination-initial-setup-configure/SKILL.md +0 -396
  226. package/skills/fsc-digital-origination-pricing-setup-coordinate/SKILL.md +0 -336
  227. package/skills/fsc-digital-origination-pricing-setup-data-foundation-create/SKILL.md +0 -208
  228. package/skills/fsc-digital-origination-pricing-setup-procedure-create/SKILL.md +0 -375
  229. package/skills/fsc-digital-origination-product-setup-act1-permissions-assign/SKILL.md +0 -83
  230. package/skills/fsc-digital-origination-product-setup-coordinate/SKILL.md +0 -248
  231. package/skills/fsc-digital-origination-sample-data-create/SKILL.md +0 -206
  232. package/skills/fsc-document-checklist-settings-configure/SKILL.md +0 -57
  233. package/skills/fsc-flexible-hierarchy-configure/SKILL.md +0 -148
  234. package/skills/fsc-flexible-hierarchy-foundation-configure/SKILL.md +0 -1430
  235. package/skills/fsc-flexible-hierarchy-graph-config-create/SKILL.md +0 -471
  236. package/skills/fsc-flexible-hierarchy-rule-based-graph-deploy/SKILL.md +0 -429
  237. package/skills/fsc-pricing-product-configurator-configure/SKILL.md +0 -150
  238. package/skills/fsc-pricing-salesforce-omnistudio-switch/SKILL.md +0 -123
  239. package/skills/fsc-product-act2-catalog-categories-create/SKILL.md +0 -82
  240. package/skills/fsc-product-act3-attributes-create/SKILL.md +0 -249
  241. package/skills/fsc-product-act4-classification-assignment-create/SKILL.md +0 -196
  242. package/skills/fsc-product-act5-considerations-get/SKILL.md +0 -44
  243. package/skills/fsc-stage-management-apex-flow-deploy/SKILL.md +0 -384
  244. package/skills/fsc-stage-management-compliant-data-sharing-configure/SKILL.md +0 -144
  245. package/skills/fsc-stage-management-decision-matrices-create/SKILL.md +0 -107
  246. package/skills/fsc-stage-management-definition-assignments-create/SKILL.md +0 -189
  247. package/skills/fsc-stage-management-definition-create/SKILL.md +0 -108
  248. package/skills/fsc-stage-management-permissions-assign/SKILL.md +0 -143
  249. package/skills/fsc-stage-management-transitions-configure/SKILL.md +0 -139
  250. package/skills/health-cloud-appt-booking-config-configure/SKILL.md +0 -135
  251. package/skills/health-cloud-appt-booking-url-config-configure/SKILL.md +0 -134
  252. package/skills/health-cloud-care-provider-affl-role-config-configure/SKILL.md +0 -136
  253. package/skills/health-cloud-care-provider-search-config-configure/SKILL.md +0 -134
  254. package/skills/health-cloud-care-requests-create/SKILL.md +0 -178
  255. package/skills/health-cloud-initial-configure/SKILL.md +0 -511
  256. package/skills/health-cloud-patient-appointment-scheduling-configure/SKILL.md +0 -1374
  257. package/skills/health-cloud-provider-search-settings-configure/SKILL.md +0 -924
  258. package/skills/health-cloud-referral-management-coordinate/SKILL.md +0 -513
  259. package/skills/health-cloud-timeline-configure/SKILL.md +0 -622
  260. package/skills/health-cloud-virtual-visit-config-configure/SKILL.md +0 -135
  261. package/skills/industries-account-score-settings-query/SKILL.md +0 -78
  262. package/skills/industries-actionable-list-data-sources-dmo-configure/SKILL.md +0 -177
  263. package/skills/industries-ad-product-availability-search/SKILL.md +0 -399
  264. package/skills/industries-advanced-account-forecast-settings-configure/SKILL.md +0 -628
  265. package/skills/industries-advanced-therapy-management-settings-configure/SKILL.md +0 -73
  266. package/skills/industries-atm-settings-starter-configure/SKILL.md +0 -76
  267. package/skills/industries-batch-document-ai-configure/SKILL.md +0 -196
  268. package/skills/industries-behavioral-health-app-settings-configure/SKILL.md +0 -71
  269. package/skills/industries-branch-management-settings-configure/SKILL.md +0 -89
  270. package/skills/industries-bre-decision-table-access-configure/SKILL.md +0 -367
  271. package/skills/industries-brokerage-settings-configure/SKILL.md +0 -41
  272. package/skills/industries-calendar-sync-settings-configure/SKILL.md +0 -89
  273. package/skills/industries-care-request-settings-configure/SKILL.md +0 -218
  274. package/skills/industries-case-management-intelligence-settings-configure/SKILL.md +0 -54
  275. package/skills/industries-catalog-process-attributes-products-configure/SKILL.md +0 -380
  276. package/skills/industries-catalog-process-fulfillment-configure/SKILL.md +0 -216
  277. package/skills/industries-catalog-process-intake-configure/SKILL.md +0 -219
  278. package/skills/industries-catalog-process-knowledge-configure/SKILL.md +0 -153
  279. package/skills/industries-catalog-process-preprocessor-configure/SKILL.md +0 -168
  280. package/skills/industries-cds-general-settings-configure/SKILL.md +0 -69
  281. package/skills/industries-claim-financial-settings-configure/SKILL.md +0 -169
  282. package/skills/industries-clinical-data-model-settings-configure/SKILL.md +0 -136
  283. package/skills/industries-clinical-data-model-settings-starter-configure/SKILL.md +0 -105
  284. package/skills/industries-collection-plan-settings-configure/SKILL.md +0 -132
  285. package/skills/industries-comms-service-console-settings-configure/SKILL.md +0 -159
  286. package/skills/industries-compliance-configure/SKILL.md +0 -102
  287. package/skills/industries-compliance-document-extraction-setup-configure/SKILL.md +0 -76
  288. package/skills/industries-contact-center-app-layout-configure/SKILL.md +0 -88
  289. package/skills/industries-contact-center-components-configure/SKILL.md +0 -96
  290. package/skills/industries-contact-center-path-knowledge-configure/SKILL.md +0 -95
  291. package/skills/industries-contact-center-user-access-assign/SKILL.md +0 -96
  292. package/skills/industries-course-waitlist-config-configure/SKILL.md +0 -162
  293. package/skills/industries-crisis-center-settings-configure/SKILL.md +0 -84
  294. package/skills/industries-custom-object-settings-configure/SKILL.md +0 -94
  295. package/skills/industries-data-cloud-integration-configure/SKILL.md +0 -88
  296. package/skills/industries-data-space-feature-mappings-configure/SKILL.md +0 -245
  297. package/skills/industries-decision-matrix-builder-configure/SKILL.md +0 -78
  298. package/skills/industries-decision-table-hbase-configure/SKILL.md +0 -87
  299. package/skills/industries-decision-table-version-configure/SKILL.md +0 -74
  300. package/skills/industries-decision-tables-builder-configure/SKILL.md +0 -253
  301. package/skills/industries-decision-tables-configure/SKILL.md +0 -439
  302. package/skills/industries-energy-utilities-configure/SKILL.md +0 -251
  303. package/skills/industries-eu-cloud-multisite-configure/SKILL.md +0 -1008
  304. package/skills/industries-eu-new-connections-configure/SKILL.md +0 -30
  305. package/skills/industries-explainability-msg-template-configure/SKILL.md +0 -135
  306. package/skills/industries-expression-set-message-token-configure/SKILL.md +0 -135
  307. package/skills/industries-field-service-settings-configure/SKILL.md +0 -82
  308. package/skills/industries-form-framework-settings-configure/SKILL.md +0 -30
  309. package/skills/industries-fsc-ai-settings-configure/SKILL.md +0 -57
  310. package/skills/industries-fsc-association-engine-settings-configure/SKILL.md +0 -172
  311. package/skills/industries-fsc-b2b-settings-configure/SKILL.md +0 -61
  312. package/skills/industries-fsc-insurance-settings-configure/SKILL.md +0 -58
  313. package/skills/industries-fsc-visa-fulfillment-setup-configure/SKILL.md +0 -103
  314. package/skills/industries-fsc-wealth-agent-settings-configure/SKILL.md +0 -117
  315. package/skills/industries-fsc-wealth-settings-configure/SKILL.md +0 -99
  316. package/skills/industries-fundraising-gift-entry-settings-configure/SKILL.md +0 -120
  317. package/skills/industries-fundraising-rfm-configure/SKILL.md +0 -128
  318. package/skills/industries-fundraising-settings-configure/SKILL.md +0 -145
  319. package/skills/industries-fundraising-source-code-gen-configure/SKILL.md +0 -299
  320. package/skills/industries-google-maps-api-key-settings-configure/SKILL.md +0 -87
  321. package/skills/industries-grantmaking-intelligence-settings-open/SKILL.md +0 -58
  322. package/skills/industries-hc-score-category-settings-configure/SKILL.md +0 -167
  323. package/skills/industries-health-engagement-settings-configure/SKILL.md +0 -218
  324. package/skills/industries-home-health-agentforce-setup-configure/SKILL.md +0 -131
  325. package/skills/industries-home-health-bundling-policy-create/SKILL.md +0 -50
  326. package/skills/industries-home-health-entity-labels-configure/SKILL.md +0 -29
  327. package/skills/industries-home-health-fsl-package-deploy/SKILL.md +0 -71
  328. package/skills/industries-home-health-fsl-settings-validate/SKILL.md +0 -27
  329. package/skills/industries-home-health-guided-setup-care-and-skill-get/SKILL.md +0 -80
  330. package/skills/industries-home-health-guided-setup-configure/SKILL.md +0 -836
  331. package/skills/industries-home-health-guided-setup-enable-org-fsl-get/SKILL.md +0 -55
  332. package/skills/industries-home-health-guided-setup-get-org-ready-get/SKILL.md +0 -61
  333. package/skills/industries-home-health-licenses-assign/SKILL.md +0 -27
  334. package/skills/industries-home-health-operating-hours-create/SKILL.md +0 -40
  335. package/skills/industries-home-health-scheduling-policy-create/SKILL.md +0 -41
  336. package/skills/industries-home-health-service-objective-create/SKILL.md +0 -22
  337. package/skills/industries-home-health-skills-create/SKILL.md +0 -31
  338. package/skills/industries-home-health-users-create/SKILL.md +0 -29
  339. package/skills/industries-icm-act3-toggles-configure/SKILL.md +0 -198
  340. package/skills/industries-icm-act5-genai-suggested-assessments-configure/SKILL.md +0 -361
  341. package/skills/industries-icm-act6-mcg-integration-configure/SKILL.md +0 -550
  342. package/skills/industries-integrated-care-management-configure/SKILL.md +0 -168
  343. package/skills/industries-object-enablement-configure/SKILL.md +0 -86
  344. package/skills/industries-tpm-system-processing-service-configure/SKILL.md +0 -64
  345. package/skills/integration-apex-settings-configure/SKILL.md +0 -61
  346. package/skills/marketing-e360-email-archiving-configure/SKILL.md +0 -73
  347. package/skills/platform-custom-permissions-configure/SKILL.md +0 -687
  348. package/skills/platform-label-workbench-import-create/SKILL.md +0 -67
  349. package/skills/platform-privacy-agent-settings-configure/SKILL.md +0 -752
@@ -1,1368 +0,0 @@
1
- ---
2
- name: communications-tmf-integrate
3
- description: "One-stop, zero-code-search reference for calling and extending Comms Cloud's TMF621 (Trouble Ticket Management) and TMF629 (Customer Management) TM Forum Open APIs — /connect/comms/troubleticket and /connect/comms/customermanagement. Trigger this skill for ANY of: calling or listing/creating/updating/deleting trouble tickets or customers via these APIs; asking what endpoints, params, request/response fields, or sub-resources (note, attachment, channel, relatedParty, engagedParty, agreement, account, etc.) exist; hitting the route directly against /connect/comms/troubleticket/v5/troubleticket or /connect/comms/customermanagement/v4/customer; questions about TMF/Comms TMF API access (CommsCloud / CommsCloudTMFAPI org permission); and — for extensibility scenarios only — writing or reviewing an Apex class that implements comms_apex_ext.ICommsGET/POST/PATCH/DELETE or the entity-specific ITroubleTicketManagementGET/POST/PATCH/DELETE / ICustomerManagementGET/POST/ PATCH/DELETE interfaces, or any of their hooks (transformRequest, configureDefaultValidations, applyCustomValidations, customiseGraphQLQuery, customiseMutationPayload, handlePostOperation), custom sub-resource registration, or deploying that Apex to an org (Setup UI, or the Metadata REST API / Tooling API). Gives exact endpoint tables, payload shapes, hook signatures, validation defaults, and a worked example — plus a built-in policy that only triggers Apex-writing/deployment when the user has explicitly asked for an extensibility scenario, always surfacing the generated class for user confirmation, always asking explicitly before deploying, and always checking for/confirming override of any existing implementor class before deploying. Never proposes or makes a schema change (new fields/objects/etc.) for TMF621/TMF629 work — always prefers extensibility when a customization is possible, explicitly tells the customer whether their request is achievable via extensibility, and says so plainly when it is not achievable within this scope. Use for keyword matches like \"TMF621\", \"TMF629\", \"trouble ticket API\", \"customer management API\", \"Comms TMF API\", or any comms_apex_ext.* interface/hook name, even if the user doesn't name the skill directly. Owned by: Comms Cloud Arches"
4
- metadata:
5
- version: "1.0"
6
- cliTools:
7
- - tool:
8
- - "sf"
9
- semver: ">=2.0.0"
10
- ---
11
-
12
- # TMF621 (Trouble Ticket) & TMF629 (Customer Management) — Complete API + Extensibility Guide
13
-
14
- This skill is organized into parts:
15
-
16
- - **Part 1 — Prerequisites** (org access, auth, API version) — read first, applies to everything.
17
- - **Part 2 — Concepts common to both APIs** (extensible pipeline, pagination, filtering/sorting mechanism).
18
- - **Part 3 — Extensibility framework** (common to both APIs) — only for extensibility scenarios.
19
- - **Part 4 — TMF621 Trouble Ticket** (API-specific: endpoints, payloads, fields).
20
- - **Part 5 — TMF629 Customer Management** (API-specific: endpoints, payloads, fields).
21
- - **Part 6 — End-to-end execution checklist.**
22
-
23
- | TMF API | Backing SObject |
24
- |---|---|
25
- | TMF621 Trouble Ticket Management | `Case` (+ `CaseTroubleTicket` extension fields for severity) |
26
- | TMF629 Customer Management | `Account` (+ `Contact`, `Contract`) |
27
-
28
- ---
29
-
30
- ## Mandatory verification gate — read this before writing any Apex
31
-
32
- **Never write or deploy an Apex extensibility class unless the user has clearly
33
- asked for extensibility.** Plain "call this API" / "list trouble tickets" /
34
- "create a customer" requests should be served entirely with Parts 4/5 (endpoint
35
- routes) — no Apex, no deployment, nothing to confirm.
36
-
37
- > **MUST — API version prerequisite for ALL extensibility scenarios (v67+):**
38
- > Extensibility is only supported on orgs whose latest supported Salesforce API
39
- > version is **`67.0` or above**. **Before running any extensibility scenario**
40
- > (writing, deploying, or reasoning about an Apex extensibility class, or any of
41
- > the hooks in Part 3), you **must** first discover the org's latest supported
42
- > API version per §1.3 (`GET .../services/data/`). If the highest version the
43
- > org reports is **below `67.0`, extensibility will not work** — stop
44
- > immediately, tell the user their org's API version is below the required
45
- > minimum (`67.0`), and do **not** write or deploy any Apex extensibility class.
46
- > This check is mandatory and applies on top of every other step in this gate.
47
- > (Plain, non-extensibility API calls in Parts 4/5 are not subject to this floor
48
- > — they use the org's own latest version per §1.3.)
49
-
50
- Follow this exact sequence whenever a request could involve extensibility:
51
-
52
- 1. **Determine intent first.** Only treat a request as an extensibility
53
- scenario if the user explicitly says so — e.g. "extend", "customize",
54
- "add a field to the response", "validate X before create", "override the
55
- response", "add a custom sub-resource", "write an Apex class for
56
- TMF621/TMF629", or similar. If the request is ambiguous (could be answered
57
- with the plain default endpoint, or you're not sure whether they want
58
- default behavior vs. custom behavior), **stop and ask the user directly**
59
- — e.g. "Do you want the default API behavior, or should I write an
60
- extensibility Apex class to customize this?" Do not guess, and do not
61
- default to writing Apex just because it's possible.
62
- 2. **Never make a schema change — ask the user to make it themselves.** Do not
63
- create/modify custom fields, objects, record types, page layouts, or any
64
- other metadata schema element to satisfy a TMF621/TMF629 request — Apex
65
- extensibility (Part 3) is the only sanctioned customization mechanism for
66
- this scope. If a request needs new schema (e.g. "store this extra value
67
- somewhere," "add a field to track X," or an extensibility class that
68
- references a field/object that doesn't exist yet), **do not create it
69
- yourself.** Tell the user exactly what schema they need to add (the object,
70
- field name, type), and ask them to make that schema change on their own.
71
- Make clear that **once they have added the schema, the request can then be
72
- supported via extensibility** — and only proceed with the Apex once the
73
- schema exists. Do not make any org changes on your own.
74
- 3. **Within TMF scope, always prefer extensibility when a customization is
75
- needed, and explicitly tell the customer whether their ask is achievable
76
- via extensibility.** For any request that isn't satisfied by the plain
77
- default endpoint, check whether one of the 6 hooks (§3.2) — or a custom
78
- sub-resource registration (§3.7) — can achieve it using fields/objects that
79
- already exist. If yes, say so explicitly and propose the extensibility
80
- approach. If the request truly cannot be done via extensibility alone,
81
- **tell the customer plainly that it is out of scope for extensibility**
82
- rather than improvising a workaround — do not silently fall back to a
83
- schema change or an out-of-scope mechanism. Two distinct cases to state
84
- explicitly to the user:
85
- - **Needs a schema change:** if the only thing blocking it is missing schema
86
- (a field/object that doesn't exist), say so, and per rule 2 ask the user
87
- to add that schema themselves — then it becomes supportable via
88
- extensibility. Do not add the schema yourself.
89
- - **Genuinely out of scope:** if it needs data or behavior outside what the
90
- 6 Apex hooks can reach (even with schema in place), tell the user plainly
91
- that it is not possible within this skill's scope, and do not attempt a
92
- workaround.
93
- 4. **Write the Apex class.** Once — and only once — the user confirms they
94
- want an extensibility scenario, write the Apex class following Part 3
95
- (correct interface from §3.1, correct hook signatures from §3.2, no-op the
96
- hooks you don't need). Show the full class to the user.
97
- 5. **After posting the Apex, explicitly prompt the user to correct anything
98
- before deploying.** Right after you show the class, ask a direct question
99
- like: "Here's the class — do you want to correct anything before I deploy
100
- it? Tell me what to change and I'll update it." Make clear the user can hand
101
- back edits or corrections, and that you will apply them **before** any
102
- deployment. Do not deploy on the same turn you first show the code. Wait for
103
- explicit confirmation ("yes / looks good / deploy it") before proceeding. If
104
- the user gives corrections, apply them, show the revised class, and ask
105
- again — repeat until the user is satisfied.
106
- 6. **Always explicitly ask the user whether they want you to deploy it** —
107
- even after they've confirmed the class is correct, deployment is a separate
108
- decision from correctness. Do not deploy just because the code was
109
- approved; ask something like "Should I deploy this to the org now?" and
110
- wait for a clear yes.
111
- 7. **Check for an existing implementor before deploying, and confirm any
112
- override.** Per §3.9, only one concrete class may implement a given
113
- `comms_apex_ext.*` interface at a time — deploying a second one causes
114
- every call for that verb+entity to fail with a discovery error.
115
- Before deploying, check (or ask the user to check) whether a class already
116
- implements the target interface (e.g. via Setup → Apex Classes, or a
117
- Tooling API query against `ApexTypeImplementor`:
118
- `GET /services/data/vXX.0/tooling/query/?q=SELECT+ClassName+FROM+ApexTypeImplementor+WHERE+InterfaceName='<Interface>'`).
119
- If one exists, tell the user its name
120
- and **explicitly confirm they want to replace/deactivate it** before
121
- proceeding — do not silently overwrite or leave both active.
122
- 8. **Deploy only after both confirmations (step 6 and step 7)**, using §3.11.
123
- 9. After deployment, re-hit the relevant route from §4.4/§5.4 (or ask the
124
- user to) to confirm the customized behavior actually shows up in the live
125
- response.
126
- 10. **Never create test data in the org on your own.** Testing an extensibility
127
- class often needs sample records (a `Case`/trouble ticket, an
128
- `Account`/customer, related `Contact`/`Contract`/`Asset`, attachments,
129
- etc.). **Do not create, insert, POST, or otherwise write any data to the
130
- org to test the Apex** — no `POST` create calls, no `sf data create`, no
131
- anonymous-Apex `insert`, nothing. First **ask the user** whether they want
132
- you to create the data needed to exercise the class, tell them exactly what
133
- you would create, and **wait for explicit confirmation**. Prefer testing
134
- against records the user already has (ask for an existing `{id}`). Only
135
- create records if the user clearly says to, and only the specific records
136
- they approved.
137
-
138
- ---
139
-
140
- # Part 1 — Prerequisites (both APIs)
141
-
142
- Org access, authentication, and API-version discovery. No code changes required.
143
-
144
- ## 1.1 Access check & required permissions
145
-
146
- Both API families are gated by a single access check that either org permission
147
- satisfies — having `CommsCloud` or `CommsCloudTMFAPI` enabled is sufficient.
148
-
149
- To call **any** endpoint in this skill, the calling user's org must have
150
- `CommsCloud` or `CommsCloudTMFAPI` org permission enabled, and the calling user
151
- needs standard CRUD/FLS access to the underlying object (`Case` for TMF621,
152
- `Account`/`Contact`/`Contract` for TMF629) — this is a Connect API resource, so
153
- ordinary Salesforce sharing and field-level security apply on top of the TMF
154
- access check.
155
-
156
- ## 1.2 Authentication (External Client App + token)
157
-
158
- Authentication is standard Salesforce session/OAuth: send a `Bearer`/session
159
- token in the `Authorization` header, exactly like any other Connect (`/services/data/vXX.0/connect/...`) REST call.
160
-
161
- **Before executing any endpoint below, the org needs an External Client App so
162
- Claude can authenticate and call these APIs on the user's behalf** (External
163
- Client Apps are Salesforce's current, recommended replacement for the legacy
164
- Connected App framework).
165
-
166
- **All External Client App details live in a dedicated reference — read it before
167
- doing any auth/app work:** the bundled reference sample **external-client-app-setup**
168
- in the skill's references bundle. It is the single source of truth for:
169
-
170
- - listing/reusing an existing External Client App (Metadata API) and the
171
- admin prerequisites — don't create a duplicate;
172
- - the inputs & defaults for creating a new app (defaults applied automatically;
173
- ask for all inputs only if the user wants to customize);
174
- - the default OAuth scopes and policies (incl. the Client Credentials run-as
175
- user requirement);
176
- - revealing/storing the Consumer Key & Secret;
177
- - obtaining an access token; and
178
- - after setup, asking the user whether to use this app to run the TMF APIs and,
179
- if so, doing everything needed to make the calls work through it (token →
180
- access check → version discovery → smoke test → the real request).
181
-
182
- In short: confirm you already hold a live token for the target org; if not,
183
- follow that reference to reuse or create the app and obtain a token. **Do not
184
- attempt any API call until both the External Client App exists and you hold a
185
- live access token for it.**
186
-
187
- ## 1.3 Discover the org's latest supported API version
188
-
189
- **Verify the APIs are reachable by discovering the org's latest supported
190
- Salesforce API version, and use that same latest version in every call in this
191
- skill.** Call `GET https://<your-instance>.my.salesforce.com/services/data/`
192
- (no version segment, with the `Bearer` token). It returns the list of every
193
- API version the org supports; take the **highest** `version` value from the
194
- response (e.g. `"67.0"`) and use it as the `v<API_VERSION>.0` / `vXX.0` segment
195
- in **every** method throughout this skill — the TMF endpoint calls (Parts 4/5),
196
- the Tooling API and Metadata REST API deploys (§3.11), the `ApexTypeImplementor`
197
- query, and any other REST call. Do not mix versions across calls. A `200` here
198
- confirms the token and instance URL are valid and tells you the exact API
199
- version to use; if this call fails, fix auth (§1.2) before going further. Always
200
- use a version the org actually reports — never hard-code a version the org may
201
- not have.
202
-
203
- ## 1.4 Base URL pattern
204
-
205
- Every endpoint in this skill is called as:
206
-
207
- ```text
208
- https://<your-instance>.my.salesforce.com/services/data/v<API_VERSION>.0<path-from-tables-below>
209
- ```
210
-
211
- Example: for TMF621's list endpoint, `<path-from-tables-below>` is
212
- `/connect/comms/troubleticket/v5/troubleticket`, so the full URL is:
213
-
214
- ```text
215
- https://myorg.my.salesforce.com/services/data/v67.0/connect/comms/troubleticket/v5/troubleticket
216
- ```
217
-
218
- The `v5` / `v4` segment in the path is the **TMF resource version** (a separate,
219
- independent version token baked into the URL itself — not the same as the
220
- Salesforce API version `v67.0` in the outer path, which comes from §1.3). Always
221
- use the exact TMF version segment shown in the tables below; using the wrong one
222
- silently 404s or routes to the legacy (non-extensible) handler.
223
-
224
- ---
225
-
226
- # Part 2 — Concepts common to both APIs
227
-
228
- ## 2.1 The extensible pipeline is always on
229
-
230
- Every endpoint in Parts 4/5 (except legacy fallbacks with no handler mapping)
231
- runs through the **extensible** code path — meaning every request, even one with
232
- zero customization, passes through the Apex extensibility pipeline described in
233
- Part 3. There is nothing you need to configure to "turn on" extensibility; it is
234
- always active, and there is no separate "legacy" endpoint you need to avoid — the
235
- framework transparently prefers the extensible handler for the versions listed in
236
- Parts 4/5. Apex customization is purely additive/optional (§3.9 explains
237
- discovery: if no Apex class implements the interface, the framework just uses
238
- defaults).
239
-
240
- ## 2.2 Pagination (bounds & per-API defaults)
241
-
242
- Both list/GET endpoints accept `offset` (int), `pageLimit` (int), and `pageinfo`
243
- (bool — return `totalPageSize`/`currentPage`). Bounds are enforced consistently
244
- for **both APIs**:
245
-
246
- - `pageLimit` max is **2000**; a larger value → HTTP 400 `"Page limit cannot exceed 2000"`.
247
- - `offset + pageLimit` must be **≤ 100,000** (it's the *sum*, not the product); exceeding it → HTTP 400 `"Pagination is supported only for 100,000 records…"`.
248
- - `offset < 0` is clamped to `0`; `pageLimit ≤ 0` falls back to the default.
249
- - **Default `pageLimit` is per-API** (applied whenever the caller omits it — the API never returns "all records" unbounded): CustomerManagement = **10**, TroubleTicket = **1000**, base default = 100.
250
-
251
- ## 2.3 Filtering & sorting — how it works
252
-
253
- The mechanism is identical for both APIs; only the named field mappings differ
254
- (see §4.5 for TMF621, §5.5 for TMF629):
255
-
256
- - **Filtering is not a fixed allow-list.** Filters are passed as dynamic query
257
- params (e.g. `?Status=New`) straight into a GraphQL `where` clause — filtering
258
- is allowed on any valid Salesforce field. Each API also has a small **named
259
- TMF-field mapping** whose TMF aliases work for filtering, sorting, and
260
- `fields=` selection; any other raw Salesforce field name on the backing object
261
- also works.
262
- - **`sort=` query param** is available at minVersion 264. Besides the flat
263
- named/raw fields, a short whitelist of nested (dotted) 1:1-relationship sort
264
- paths is allowed per API.
265
- - **Multi-record/child connections are not sortable** via the parent list
266
- (e.g. notes, attachments, status history, child accounts, contracts) — they
267
- are GraphQL connections, not scalar columns.
268
- - The Workbench artifact param `t` is explicitly stripped, and any filter with a
269
- `null` field name or `null` value is silently dropped.
270
- - **A bad filter field fails silently as an empty result — not an error.** The
271
- raw field name is dropped **untranslated** into the GraphQL `where` clause, so
272
- it must be a valid, GraphQL-queryable field on the backing object. If you pass
273
- a field that doesn't exist (or isn't queryable), the GraphQL layer errors and
274
- the API **returns an empty result set rather than a 4xx**. So an empty list can
275
- mean "no matching records" **or** "you filtered on a field name that doesn't
276
- exist" — warn the user of this when a filter unexpectedly returns nothing.
277
-
278
- ### 2.3.1 When a user wants to filter by a field that isn't an out-of-the-box named filter
279
-
280
- If a user asks to filter by something not in the named filter/mapping table
281
- (§4.5/§5.5), **ask them which exact field they mean**, then follow this decision
282
- flow — do not guess:
283
-
284
- 1. **Is it a field that exists directly on the backing object** (`Case` for
285
- TMF621, `Account` for TMF629)? Confirm the field is really present on that
286
- object (ask the user, or check the org schema — do not assume). If yes:
287
- - **No extensibility needed.** Tell the user they can filter by the raw
288
- Salesforce field name directly as a query param — e.g.
289
- `?Origin=Phone` (TMF621) or `?Industry=Telecom` (TMF629). It works out of
290
- the box (verified: unmapped field names pass straight into the `where`
291
- clause). Only offer extensibility if they *also* want to expose it under a
292
- different TMF-facing name (see step 2).
293
- 2. **Do they want to filter under a custom/renamed TMF name, or the field is on
294
- a related/child object** (not a scalar column on the backing object)? Then it
295
- needs **extensibility** (`customiseGraphQLQuery` — add a filter to the right
296
- node, or map the incoming name). If feasible:
297
- - Confirm feasibility, then **ask the customer which Salesforce field they
298
- want that TMF filter name mapped to**, and write the extensibility class
299
- using that mapping (per the mandatory gate + §3.4). Substitute their real
300
- field/relationship API names.
301
- 3. **Is the field NOT present on the entity at all** (no such field on the
302
- object, and not reachable via a relationship the hooks can query)? Then
303
- **plainly tell the user it cannot be done because the field itself does not
304
- exist on that entity** — do not invent a workaround, and (per rule 2) do not
305
- create the field yourself. If they want it, they must add the schema first
306
- (then it becomes filterable per step 1).
307
-
308
- ---
309
-
310
- # Part 3 — Extensibility framework (common to both APIs)
311
-
312
- This is the **only** mechanism intended for customization: an Apex class
313
- implementing a well-known interface, deployed to the org. There is no Java code
314
- change, no config file, no registry entry needed — the framework discovers your
315
- Apex class by its **interface name** at request time. Only enter this part for
316
- an extensibility scenario, and only after the mandatory gate at the top of this
317
- skill.
318
-
319
- ## 3.1 The interface family tree
320
-
321
- ```text
322
- comms_apex_ext.ICommsExtensible (base — declares transformRequest, configureDefaultValidations,
323
- applyCustomValidations, customiseGraphQLQuery, handlePostOperation
324
- — ALL 5 methods, inherited by every verb below, GET included)
325
- ├── comms_apex_ext.ICommsGET extends ICommsExtensible (no extra methods)
326
- │ ├── comms_apex_ext.ITroubleTicketManagementGET extends ICommsGET ← implement for TMF621 GET
327
- │ └── comms_apex_ext.ICustomerManagementGET extends ICommsGET ← implement for TMF629 GET
328
- ├── comms_apex_ext.ICommsPOST extends ICommsExtensible (+ customiseMutationPayload)
329
- │ ├── comms_apex_ext.ITroubleTicketManagementPOST extends ICommsPOST ← implement for TMF621 POST
330
- │ └── comms_apex_ext.ICustomerManagementPOST extends ICommsPOST ← implement for TMF629 POST
331
- ├── comms_apex_ext.ICommsPATCH extends ICommsExtensible (+ customiseMutationPayload)
332
- │ ├── comms_apex_ext.ITroubleTicketManagementPATCH extends ICommsPATCH ← implement for TMF621 PATCH
333
- │ └── comms_apex_ext.ICustomerManagementPATCH extends ICommsPATCH ← implement for TMF629 PATCH
334
- └── comms_apex_ext.ICommsDELETE extends ICommsExtensible (+ customiseMutationPayload)
335
- ├── comms_apex_ext.ITroubleTicketManagementDELETE extends ICommsDELETE ← implement for TMF621 DELETE
336
- └── comms_apex_ext.ICustomerManagementDELETE extends ICommsDELETE ← implement for TMF629 DELETE
337
- ```
338
-
339
- **Important — `customiseGraphQLQuery` is declared on the base `ICommsExtensible`
340
- interface, not added by `ICommsGET`.** That means it is inherited by every
341
- verb's interface, including POST/PATCH/DELETE. Apex requires implementing
342
- every method an interface declares or inherits, so a POST/PATCH/DELETE Apex
343
- class **must** also implement `customiseGraphQLQuery` (no-op it by returning
344
- `null` — see §3.2) or the class will fail to compile. In practice it only has
345
- real effect on GET, since POST/PATCH/DELETE don't run a query — but it still
346
- must be present in the class body for any verb.
347
-
348
- **Rule of thumb: pick exactly one interface per HTTP verb you want to customize**,
349
- implement it in a single Apex class (a class *can* implement more than one of
350
- these interfaces at once, e.g. both GET and POST, if you want one class to
351
- handle multiple verbs — see the "combined pattern" example in §3.8). **All 5
352
- methods declared on `ICommsExtensible` must be implemented for every verb**
353
- (plus `customiseMutationPayload` for POST/PATCH/DELETE), even if you just want
354
- to no-op them — return the "do nothing" value shown per hook below.
355
-
356
- There is **no sub-resource-specific interface** — sub-resource requests
357
- (`GET .../{id}/{subResource}`) are routed through the *same* `ICommsGET`
358
- implementation as the plain single-entity GET; you distinguish sub-resource
359
- requests inside your hooks by checking `context.get("subResourceName")` /
360
- registering custom sub-resource mappings (§3.7).
361
-
362
- ## 3.2 Every hook method, in call order
363
-
364
- The framework calls your hooks in this fixed pipeline order for every request.
365
- **If you don't implement a behavior for a hook, return the pass-through/no-op
366
- value shown — do not return `null` unless explicitly allowed, as some hooks
367
- treat `null` as "no data to merge" rather than "no-op".**
368
-
369
- | Order | Hook | Verbs | Signature | Purpose | No-op return |
370
- |---|---|---|---|---|---|
371
- | 1 | `transformRequest` | GET/POST/PATCH/DELETE | `Map<String,Object> transformRequest(Map<String,Object> context)` | First hook called. Mutate the request context before anything else runs — e.g. resolve an external ID to a Salesforce Id, inject custom flags/params used by later hooks. | `return context;` |
372
- | 2 | `configureDefaultValidations` | GET/POST/PATCH/DELETE | `Map<String,Boolean> configureDefaultValidations(Map<String,Boolean> defaultValidationConfiguration, Map<String,Object> context)` | Turn built-in field validators on/off per request (§3.6). | `return defaultValidationConfiguration;` (or `new Map<String,Boolean>()` to accept framework defaults unchanged) |
373
- | 3 | `applyCustomValidations` | GET/POST/PATCH/DELETE | `Map<String,Object> applyCustomValidations(Map<String,Object> context)` | Run your own validation logic. To **fail** the request, return a map shaped like the error example below; the framework throws and the HTTP call gets a 400. | `return null;` |
374
- | 4a | `customiseGraphQLQuery` | GET/POST/PATCH/DELETE (declared on base `ICommsExtensible`; only has functional effect on GET, but must still be implemented for every verb) | `Map<String,Object> customiseGraphQLQuery(Map<String,Object> graphQLAsMap, Map<String,Object> context)` | Add/remove fields, add/merge filters, set sort order on the query the framework is about to run. | `return null;` |
375
- | 4b | `customiseMutationPayload` | POST/PATCH/DELETE only | `Map<String,Object> customiseMutationPayload(Map<String,Object> mutationGraphQLPayload, Map<String,Object> context)` | Add extra fields/values to the create/update/delete mutation before it executes. | `return null;` |
376
- | 5 | (framework executes the GraphQL query/mutation — not customizable) | — | — | — | — |
377
- | 6 | `handlePostOperation` | GET/POST/PATCH/DELETE (called on every verb, but the returned payload override is only applied on GET — see §3.3) | `Map<String,Object> handlePostOperation(Map<String,Object> graphQLQueryResultAsMap, Map<String,Object> constructedTMFResponse, Map<String,Object> context)` | Last hook. Inspect the raw GraphQL result and/or the TMF-shaped response the framework built, and on GET optionally **replace it entirely**. | `return null;` |
378
-
379
- Note `applyCustomValidations` and `handlePostOperation` share the same name
380
- across GET/POST/PATCH/DELETE (defined once on the base `ICommsExtensible`), so
381
- implementing a POST class means implementing them for the POST flow, etc. — the
382
- framework calls whichever concrete class is currently active for that verb.
383
-
384
- Hook methods may be declared **either `global static` or as instance
385
- (`global`/`public`) methods** — the framework instantiates your class and then
386
- invokes the method, so both styles compile and run. The examples
387
- below use `global static`; instance methods work identically.
388
-
389
- Quick "which hook do I use" index:
390
-
391
- | Goal | Hook(s) |
392
- |---|---|
393
- | Resolve an external id (CaseNumber, AccountNumber) to a Salesforce Id | `transformRequest` |
394
- | Enforce a custom business rule / access check before running | `applyCustomValidations` |
395
- | Role-based data filtering (block, then trim what's returned) | `applyCustomValidations` + `handlePostOperation` |
396
- | Add fields / server-side filter / sort to a GET query | `customiseGraphQLQuery` |
397
- | Add fields to a create/update mutation | `customiseMutationPayload` |
398
- | Enrich or restructure the response (calculated fields, GET-only full override) | `handlePostOperation` |
399
-
400
- `transformRequest` receives (and must return) the same `context` map passed to
401
- every later hook. The framework reads two keys back out of it after the call:
402
- `context.get('id')` (a `String`) overwrites the path id used for the rest of
403
- the request, and `context.get('requestBody')` (a `Map`) overwrites the request
404
- body — this is how you resolve an alternate identifier (e.g. an external
405
- account/order number) to a Salesforce Id before any query or mutation runs:
406
-
407
- ```java
408
- global static Map<String, Object> transformRequest(Map<String, Object> context) {
409
- String rawId = (String) context.get('id');
410
- if (rawId != null && !rawId.startsWith('001')) { // not already a Salesforce Account Id
411
- List<Account> matches = [SELECT Id FROM Account WHERE AccountNumber = :rawId LIMIT 1];
412
- if (!matches.isEmpty()) {
413
- context.put('id', matches[0].Id);
414
- }
415
- }
416
- return context;
417
- }
418
- ```
419
-
420
- ## 3.3 `handlePostOperation` — the two response-shaping outcomes
421
-
422
- `handlePostOperation` is the most commonly used hook. It always receives:
423
-
424
- - `graphQLQueryResultAsMap` — the raw GraphQL response (`{"data": {"uiapi": {"query": {...}}}}` shape for GET, or `{"data": {"uiapi": {<alias>: {...}}}}` for mutations).
425
- - `constructedTMFResponse` — the TMF-shaped payload the framework already built (e.g. `{"id": "...", "name": "...", "href": "..."}` for single-record GET, or `{"recordList": [...]}` for list GET).
426
- - `context` — the (possibly already transformed) request context.
427
-
428
- Return one of:
429
-
430
- **A. No change** — return `null`. The framework's `constructedTMFResponse` is used as-is.
431
-
432
- **B. Full override** — return:
433
- ```java
434
- new Map<String, Object>{
435
- 'payloadOverwritten' => true,
436
- 'updatedPayload' => myModifiedResponseMap // must be a complete replacement, not a diff
437
- }
438
- ```
439
- When `payloadOverwritten=true`, the framework serializes exactly `updatedPayload`
440
- back to the caller as the response body (surfaced as the `updatedPayload` field
441
- on the output representation — see §4.2/§5.2). This is how the reference example
442
- in §3.8 renames `id`→`salesforce_id` and injects an extra field.
443
-
444
- **This override only works on GET.** `handlePostOperation`'s return value is
445
- only read back and applied on the GET path. On POST, PATCH,
446
- and DELETE the framework still *calls* your `handlePostOperation` (so
447
- side-effecting logic — logging, firing a platform event, calling out to another
448
- system — still runs), but the map you return is discarded; there is no
449
- supported way to overwrite the POST/PATCH/DELETE response body from this hook.
450
- If you need to change what a mutation returns, do it via `customiseMutationPayload`
451
- (§3.5) before the mutation runs, not via `handlePostOperation` after.
452
-
453
- ## 3.4 `customiseGraphQLQuery` — instruction map shape (GET)
454
-
455
- Return a map with a `nodes` list; each entry targets one GraphQL query node by
456
- its `path` alias (`troubleTicket` for TMF621, `troubleTicket`/`customerAccount`
457
- depending on handler — check the raw query in `graphQLAsMap.get("query")` to
458
- find the exact alias for the entity you're customizing):
459
-
460
- ```java
461
- global static Map<String, Object> customiseGraphQLQuery(Map<String,Object> querySpec, Map<String,Object> context) {
462
- return new Map<String, Object>{
463
- 'nodes' => new List<Object>{
464
- new Map<String, Object>{
465
- 'path' => 'troubleTicket', // GraphQL alias to target
466
- 'addFields' => new List<String>{ 'IsClosed { value }' }, // extra fields to select
467
- 'addFilters' => new Map<String, Object>{
468
- 'clearExistingFilters' => false, // false = merge with existing filters
469
- 'filterGraphQlSnippet' => '{ IsClosed: { eq: true } }'
470
- },
471
- 'orderBy' => new Map<String, Object>{
472
- 'clearExistingOrderBy' => true, // true = replace any client-requested sort
473
- 'orderGraphQlSnippet' => '{ Subject: { order: ASC } }'
474
- }
475
- }
476
- }
477
- };
478
- }
479
- ```
480
-
481
- `addFields` entries are raw GraphQL field-selection snippets appended under the
482
- targeted node — use `{ value }` suffix for scalar fields per UI API convention
483
- (e.g. `'IsClosed { value }'`, not just `'IsClosed'`).
484
-
485
- ## 3.5 `customiseMutationPayload` — instruction map shape (POST/PATCH/DELETE)
486
-
487
- ```java
488
- global static Map<String, Object> customiseMutationPayload(Map<String,Object> mutationGraphQLPayload, Map<String,Object> context) {
489
- return new Map<String, Object>{
490
- 'nodes' => new List<Object>{
491
- new Map<String, Object>{
492
- 'path' => 'customerAccount', // mutation alias to target, e.g. AccountCreate/AccountUpdate/AccountDelete
493
- 'addFields' => new Map<String, Object>{ // for mutations, addFields is a map of field->value, not a list
494
- 'Industry' => 'Technology',
495
- 'Revenue' => 5000000,
496
- 'Status' => 'Active'
497
- }
498
- }
499
- }
500
- };
501
- }
502
- ```
503
-
504
- A mutation node supports these keys (all optional except `path`):
505
-
506
- | Key | Shape | Effect |
507
- |---|---|---|
508
- | `addFields` | `Map<String,Object>` (field→value) | Adds brand-new fields/values to the mutation input for that node. |
509
- | `modifyInput` | `Map` with `inputModifications` / `addInputFields` / `removeInputFields` | Rewrite fields already in the input. `inputModifications` (field→new value) overwrites values the caller sent — e.g. `{'Name' => 'Updated ' + originalName}`; `removeInputFields` is a `List<String>` of fields to drop. |
510
- | `insertGraphQlSnippet` | `String` (raw GraphQL) | Inject an extra mutation node — e.g. create a related custom-object record. Use `@{alias}` to reference the id of another node created in the same mutation (e.g. `Account__c: "@{customerAccount}"`). |
511
- | `updateGraphQlSnippet` | `String` (raw GraphQL) | Same, for an update sub-mutation. |
512
-
513
- Example — overwrite the caller-supplied `Name` and create a related record in one call:
514
- ```java
515
- global static Map<String, Object> customiseMutationPayload(Map<String,Object> request, Map<String,Object> context) {
516
- Map<String,Object> body = (Map<String,Object>) context.get('requestBody');
517
- String name = body == null ? null : (String) body.get('name');
518
- if (String.isBlank(name)) { return new Map<String, Object>(); } // no-op → return empty map, not null
519
- return new Map<String, Object>{
520
- 'nodes' => new List<Object>{
521
- new Map<String, Object>{
522
- 'path' => 'customerAccount',
523
- 'modifyInput' => new Map<String, Object>{
524
- 'inputModifications' => new Map<String, Object>{ 'Name' => 'Updated ' + name }
525
- }
526
- },
527
- new Map<String, Object>{
528
- 'path' => 'customerAccount.extCustomer',
529
- 'insertGraphQlSnippet' => 'extCustomer: ExtCustomer__cCreate(input: { ExtCustomer__c: { Name: "'
530
- + name.replace('\\', '\\\\').replace('"', '\\"')
531
- + '", Account__c: "@{customerAccount}" } }) { Record { Id } }'
532
- }
533
- }
534
- };
535
- }
536
- ```
537
-
538
- ## 3.6 Built-in field validators you can toggle with `configureDefaultValidations`
539
-
540
- Each API has its own set of named boolean validators; pass a map with the names
541
- you want to flip. Any name you omit keeps the framework's default for that verb
542
- (shown below — POST defaults to all `true`, PATCH defaults to all `false`).
543
-
544
- **`configureDefaultValidations` only has an effect on POST/PATCH/DELETE.** On
545
- GET the hook is still invoked (so it must be implemented), but the map you
546
- return is ignored — GET ships an empty default validation spec and never runs
547
- these named validators. Use `applyCustomValidations` (§3.2) if you need to
548
- reject a GET request.
549
-
550
- **TMF621 (TroubleTicket)** — validator names: `mandatoryFieldsValidation`
551
- (requires `description`, `severity`, `ticketType`), `relatedPartyValidation`
552
- (each `relatedParty` entry needs non-blank `id` and `referredType` ∈
553
- {`Account`,`Contact`}), `relatedEntityValidation` (each `relatedEntity` entry
554
- needs non-blank `id` and `referredType` == `Asset`).
555
- - POST default: `{mandatoryFieldsValidation: true, relatedPartyValidation: true, relatedEntityValidation: true}`
556
- - PATCH default: `{mandatoryFieldsValidation: false, relatedPartyValidation: false, relatedEntityValidation: false}`
557
-
558
- **TMF629 (Customer)** — validator names: `mandatoryFieldsValidation`,
559
- `engagedPartyValidation`, `contactMediumValidation`, `accountValidation`.
560
- - POST default: `{mandatoryFieldsValidation: true, engagedPartyValidation: true, contactMediumValidation: true, accountValidation: true}`
561
- - PATCH default: `{mandatoryFieldsValidation: false, engagedPartyValidation: false, contactMediumValidation: false, accountValidation: false}`
562
-
563
- Example — disable the mandatory-fields check on TMF621 POST:
564
- ```java
565
- global static Map<String, Boolean> configureDefaultValidations(
566
- Map<String, Boolean> defaultValidationConfiguration, Map<String, Object> context) {
567
- Map<String, Boolean> updated = new Map<String, Boolean>(defaultValidationConfiguration);
568
- updated.put('mandatoryFieldsValidation', false);
569
- return updated;
570
- }
571
- ```
572
-
573
- To **fail** a request from `applyCustomValidations` instead, return
574
- `validationStatus`/`validationMessage`/`validationDetails` as **top-level**
575
- keys (no extra wrapper) — this is exactly what the framework reads back when it
576
- invokes `applyCustomValidations`:
577
- ```java
578
- global static Map<String, Object> applyCustomValidations(Map<String, Object> context) {
579
- Boolean isBad = /* your check */ false;
580
- if (isBad) {
581
- return new Map<String, Object>{
582
- 'validationStatus' => 'fail',
583
- 'validationMessage' => 'Custom validation failure',
584
- 'validationDetails' => new Map<String, Object>{
585
- 'statusCode' => 'BAD_REQUEST',
586
- 'errorCode' => 'BAD_REQUEST'
587
- }
588
- };
589
- }
590
- return null;
591
- }
592
- ```
593
- `validationStatus` is checked case-insensitively against `"fail"`; on a match
594
- the framework throws and the call gets a 400 with `validationMessage` (default
595
- `"Validation failed"`) and `validationDetails` in the error body.
596
-
597
- ## 3.7 Registering a brand-new custom sub-resource (no Java change)
598
-
599
- Do this entirely from `transformRequest` on your `*GET` implementation: put a
600
- `customSubResourceMappings` entry into the context, keyed by the sub-resource
601
- name you want exposed in the URL, describing the SOQL-level relationship:
602
-
603
- ```java
604
- global static Map<String, Object> transformRequest(Map<String, Object> context) {
605
- context.put('customSubResourceMappings', new Map<String, Object>{
606
- 'opportunities' => new Map<String, Object>{
607
- 'sfChildObjectName' => 'Opportunity',
608
- 'sfParentLookupField' => 'AccountId',
609
- 'sfRelationshipName' => 'Opportunities'
610
- }
611
- });
612
- return context;
613
- }
614
- ```
615
-
616
- Then in `customiseGraphQLQuery`, inject the raw GraphQL connection snippet for
617
- that relationship (`insertGraphQlSnippet` instruction), and in
618
- `handlePostOperation`, read `context.get("isCustomSubResource")` /
619
- `context.get("customSubResourceRawRecords")` and build the TMF JSON yourself
620
- (the framework defers all shaping of custom sub-resources to your Apex — it
621
- only knows how to shape the built-in ones listed in §4.3/§5.3).
622
-
623
- ## 3.8 Worked examples & pattern sketches — adapt, don't copy verbatim
624
-
625
- > **⚠️ Read before using anything in §3.8.x.** The Apex classes in §3.8, §3.8.1,
626
- > and §3.8.2 are **illustrative samples to learn the mechanics from — they are
627
- > NOT the source of truth and NOT copy-paste-ready.** They encode field names,
628
- > object names, validation rules, and business logic that were invented for the
629
- > example, not derived from the user's org. **Do not deploy any of them as-is.**
630
- >
631
- > When a user asks for an extensibility class:
632
- > 1. **First understand what the user actually wants** — which verb (GET/POST/
633
- > PATCH/DELETE), which entity (TMF621 vs TMF629), which hooks, what fields/
634
- > objects/rules in *their* org, and what the desired behavior is. Ask if it's
635
- > ambiguous (per the mandatory verification gate at the top of this skill).
636
- > 2. **Then write a fresh class tailored to that request**, using these samples
637
- > only as a reference for the correct interface (§3.1), hook signatures
638
- > (§3.2), return-value contracts (§3.3–§3.6), and the `'nodes'`/`addFields`/
639
- > `insertGraphQlSnippet`/`@{alias}` mechanics. Substitute the user's real
640
- > field/object API names — never keep the sample placeholders.
641
- > 3. **Verify and deploy** per the §3.11 workflow, then re-hit the endpoint to
642
- > confirm the behavior.
643
- >
644
- > The source of truth is always the framework code and the user's org schema —
645
- > not these snippets.
646
-
647
- ### 3.8.0 Full worked example — combine multiple patterns in one class
648
-
649
- This is a complete, deployable-shaped Apex class for **TMF621 GET** that (1) adds
650
- an extra field to the response, (2) filters server-side, (3) sorts, and (4)
651
- renames a response field — demonstrating that all four extensibility axes can be
652
- combined freely in one class. **Adapt the field names and logic to the user's
653
- org before deploying (see the §3.8 banner).**
654
-
655
- **Full source:** the bundled reference sample **TroubleTicketCombinedPattern**
656
- — read that file in the skill's references/apex-samples bundle for the complete class. It implements
657
- `comms_apex_ext.ITroubleTicketManagementGET`, targets the `troubleTicket` query
658
- node in `customiseGraphQLQuery` (adds `IsClosed { value }`, a
659
- `clearExistingFilters=false` filter, and an `orderBy`), and in
660
- `handlePostOperation` unwraps the `data → uiapi → query → troubleTicket → edges`
661
- shape to rename `id` → `salesforce_id` and copy `IsClosed` onto each record via a
662
- `payloadOverwritten`/`updatedPayload` override.
663
-
664
- Deploy this class to your org (Setup → Apex Classes, or via the Metadata REST
665
- API / Tooling API — see §3.11), then simply call the TMF621 GET/list endpoints
666
- as shown in §4.4 — no additional registration step. The framework finds it
667
- automatically (see §3.9).
668
-
669
- ### 3.8.1 Pattern sketch — advanced TMF629 GET (external-key resolution + supplementary SOQL + custom sub-resources)
670
-
671
- > **⚠️ Pattern sketch, not a verified drop-in.** Every construct below was
672
- > checked against the framework contract (hook signatures, the `'nodes'` /
673
- > `clearExistingFilters` / `filterGraphQlSnippet` keys, the alias-based `path`,
674
- > the `data → uiapi → query → customerAccount → edges` shape, the
675
- > `payloadOverwritten`/`updatedPayload` contract, the filter fields
676
- > `fieldName`/`value`, and the case-insensitive `"fail"` check).
677
- > **It has NOT been compiled or deployed to an org**, so treat it as an
678
- > illustrative composition, not copy-paste-ready code. Before using it, follow
679
- > the §3.11 workflow: deploy it, hit `GET /connect/comms/customermanagement/v4/customer/{id}`,
680
- > and confirm the enrichment actually appears — then fix whatever the live run
681
- > surfaces. Field/relationship API names (`AccountNumber`, `Opportunities`,
682
- > `Contract.ContractNumber`) are placeholders; substitute your org's real ones.
683
-
684
- This sketch composes three patterns the earlier examples only show in isolation:
685
-
686
- 1. **`transformRequest`** — accept a business key (`AccountNumber`) in the
687
- `{id}`/filter slot and rewrite the filter's `value` to the real `Account` Id
688
- (the framework maps a path `{id}` into a filter whose `fieldName` is `Id`).
689
- 2. **`customiseGraphQLQuery`** — add scalar fields plus a related child
690
- connection (`Opportunities`) to the `customerAccount` node.
691
- 3. **`handlePostOperation`** — run supplementary SOQL the GraphQL query can't
692
- express (latest `ContractNumber` per account), then inject a brand-new
693
- `relatedOpportunities` sub-resource and drop an OOTB field — handling **both**
694
- the list (`recordList`) and single-record (`id`) response shapes.
695
-
696
- **Full source:** the bundled reference sample **CustomerManagementAdvancedGet**
697
- — read that file in the skill's references/apex-samples bundle for the complete class, including the private `enrichRecord` /
698
- `extractOpportunities` / `graphQLValue` helpers.
699
-
700
- **Two things this sketch deliberately does NOT do** (both were present in a
701
- source doc but are wrong against the code, so don't reintroduce them):
702
- `customiseGraphQLQuery` must return `'nodes'` (a `'spec'` key is silently
703
- ignored); and a transformation `path` must be a GraphQL **alias**
704
- (`customerAccount`), never a dotted `uiapi.query.Account` — structural fields
705
- (`uiapi`, `query`, `edges`, `node`, `pageInfo`) are stripped during path
706
- resolution. The filter object provided to `transformRequest` also has **only**
707
- the keys `fieldName`, `operator`, `value`, and `dataType` — there is no
708
- `soqlcondition`/`rawValue` sub-map to mutate.
709
-
710
- ### 3.8.2 Pattern sketch — advanced TMF629 POST (custom validations + related-record mutation + side-effect DML)
711
-
712
- > **⚠️ Pattern sketch, not a verified drop-in** (see the §3.8 banner — adapt to
713
- > the user's request/org, don't copy verbatim). Every construct below was
714
- > checked against the framework contract (the `customiseMutationPayload`
715
- > `'nodes'` shape with **`addFields` as a `Map`**; per-node
716
- > `insertGraphQlSnippet`; the `@{customerAccount}` back-reference; the
717
- > `validationStatus: "fail"` case-insensitive reject; and the POST
718
- > post-processing flow). **It has NOT been compiled or deployed.**
719
- > Field/relationship API names
720
- > (`NumberOfEmployees`, `Website`, `MyChild__c`, etc.) are placeholders —
721
- > substitute the user's real ones. Deploy and hit
722
- > `POST /connect/comms/customermanagement/v4/customer` per §3.11 before trusting it.
723
-
724
- **Two POST-specific gotchas this sketch exists to teach — both differ from GET:**
725
-
726
- - **`handlePostOperation`'s return value is IGNORED for POST/PATCH/DELETE.**
727
- The framework calls the hook but discards its result — there is **no
728
- `payloadOverwritten`/`updatedPayload` response-override for mutations** (that
729
- only works on GET, §3.3). On a mutation, `handlePostOperation` is only useful
730
- for **side effects** (extra DML). Anything you `return` vanishes.
731
- - **The hook's 2nd parameter is the REQUEST BODY on a mutation, not the TMF
732
- response.** For GET it's the constructed TMF response; for POST/PATCH/DELETE
733
- the framework passes the request body map as the 2nd parameter. Name it
734
- accordingly.
735
-
736
- Also note: for a **mutation**, `addFields` is a **`Map`** (field → value); for a
737
- **query** (`customiseGraphQLQuery`, §3.4) `addFields` is a **`List`** of field
738
- strings. Same key name, different shape per hook.
739
-
740
- **Full source:** the bundled reference sample **CustomerManagementAdvancedPost**
741
- — read that file in the skill's references/apex-samples bundle for the complete class, including the `customiseMutationPayload`
742
- node list (extra `Account` fields + a `MyChild__cCreate` snippet back-referencing
743
- `@{customerAccount}`), the side-effect `insert Opportunity` in
744
- `handlePostOperation`, and the `escape` helper.
745
-
746
- > **Governor-limit caution.** `applyCustomValidations` runs SOQL and
747
- > `handlePostOperation` runs DML on **every** POST. Keep these minimal and
748
- > bulk-safe; a heavy hook multiplies per request and can blow limits under load.
749
-
750
- ## 3.9 How your Apex class gets discovered — no registry to update
751
-
752
- There is **no file, XML, or metadata entry to register your Apex class**. At
753
- request time the framework:
754
-
755
- 1. Takes the interface name for the current verb+entity (e.g.
756
- `comms_apex_ext.ITroubleTicketManagementGET`).
757
- 2. Queries the org's compiled Apex metadata (`ApexTypeImplementor`) for
758
- **concrete classes that implement that interface**.
759
- 3. If exactly one is found → uses it.
760
- 4. If none is found → skips all Apex hooks; the endpoint behaves with pure
761
- framework defaults (this is why every endpoint in Parts 4/5 works even with
762
- zero Apex deployed).
763
- 5. If more than one concrete class implements the same interface → the request
764
- **fails** with a discovery error. Only ever have one active
765
- implementor per interface. If you need to swap implementations, delete/deactivate
766
- the old one before deploying the new one.
767
-
768
- This means: to extend behavior, you only ever need to **write and deploy one
769
- Apex class per verb you want to customize** — nothing else in the platform
770
- needs to change.
771
-
772
- ## 3.10 Permissions on your Apex extension classes
773
-
774
- Every `comms_apex_ext.*` interface is guarded so that it is only invocable in
775
- orgs that already pass the base TMF access check from §1.1 (i.e. have the
776
- `CommsCloud` or `CommsCloudTMFAPI` org permission), and has a minimum API
777
- version of `260` (`262` for the TroubleTicket family). This means you do not
778
- need to grant any additional permission specifically for the extensibility
779
- classes themselves.
780
-
781
- ## 3.11 Deploying your extensibility Apex class to the org
782
-
783
- **Before deploying, confirm both:** (a) the user explicitly wants you to
784
- deploy now (not just that the code looks correct), and (b) whether an existing
785
- class already implements the target interface — if so, get explicit
786
- confirmation to override/deactivate it. See the mandatory gate steps 6 and 7 at
787
- the top of this skill.
788
-
789
- **Also re-confirm the org meets the v67+ prerequisite** from the mandatory gate
790
- before deploying — if the org's latest supported API version (§1.3) is below
791
- `67.0`, extensibility will not work; stop and tell the user rather than
792
- deploying.
793
-
794
- **API version floor for extensibility Apex: `67.0` minimum.** Every
795
- extensibility class's `apiVersion` (in its `.cls-meta.xml`, or in any
796
- Metadata/Tooling API call used to deploy it) must be set to **`67.0` or
797
- higher** — never lower. This applies only to the Apex class's own API version;
798
- it does not change the TMF resource version segments (`v5`/`v4`) or the
799
- outer Connect API version used when *calling* the endpoints in Parts 4/5.
800
-
801
- Your extensibility class is deployed **straight into the target Salesforce org**
802
- — it is ordinary customer-facing Apex, not part of this Bazel repo, and it does
803
- not go through any Bazel build/CI pipeline. **Prefer the API-based deploys
804
- (Option A, then Option B); fall back to the Setup UI (Option C) only if neither
805
- API deploy is available or both fail.** Use the org's authenticated `Bearer`
806
- token (Part 1) on every REST call below, and substitute `vXX.0` with the org's
807
- **latest** supported API version discovered in §1.3 (the same version used for
808
- every other call in this skill):
809
-
810
- **Option A (preferred) — Tooling API, for a scripted single-class create/update:** POST the class to the Tooling API's `ApexClass` sobject endpoint. The `Body` is the full class source and `ApiVersion` must be `67.0` or higher (per the floor above):
811
-
812
- ```text
813
- POST https://<MyDomainName>.my.salesforce.com/services/data/vXX.0/tooling/sobjects/ApexClass
814
- Authorization: Bearer <access_token>
815
- Content-Type: application/json
816
-
817
- { "Body": "<full Apex class source>", "ApiVersion": 67.0, "Status": "Active" }
818
- ```
819
-
820
- To **replace** an existing class, you cannot directly `PATCH` an `ApexClass` in production-like orgs — instead use the Tooling API `MetadataContainer` + `ApexClassMember` + `ContainerAsyncRequest` deploy sequence, or use Option B. A `200`/`201` returning the new record `Id` means the compile succeeded; a compile error comes back as a `400` with the error body — report it and offer to fix.
821
-
822
- **Option B — Metadata REST API deploy, for a scripted multi-component or replace-in-place deploy (CI, admin scripts):** package the class + its `.cls-meta.xml` (with `<apiVersion>67.0</apiVersion>` or higher) and a `package.xml` into a base64-encoded zip and POST it to the Metadata REST `deployRequest` endpoint:
823
-
824
- ```text
825
- POST https://<MyDomainName>.my.salesforce.com/services/data/vXX.0/metadata/deployRequest
826
- Authorization: Bearer <access_token>
827
- Content-Type: application/json
828
-
829
- { "deployOptions": { "singlePackage": true }, "file": "<base64-encoded zip>" }
830
- ```
831
-
832
- Poll `GET /services/data/vXX.0/metadata/deployRequest/<id>?includeDetails=true` until `deployResult.status` is `Succeeded` (or `Failed`, with component errors in the details). The `.cls-meta.xml` sets the class's own `apiVersion` (must be `67.0`+):
833
- ```xml
834
- <?xml version="1.0" encoding="UTF-8"?>
835
- <ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
836
- <apiVersion>67.0</apiVersion>
837
- <status>Active</status>
838
- </ApexClass>
839
- ```
840
-
841
- **Option C (last resort) — Setup UI, only when no API-based deploy is available (no scripted/token access) or both API options above have failed:**
842
-
843
- 1. In the target org, go to **Setup → (Quick Find) Apex Classes**.
844
- 2. Click **New**.
845
- 3. Paste the full class body (the exact code shown to and confirmed by the user in step 2/3 of the workflow above).
846
- 4. Click **Save**. Any compile errors are shown inline — report them back to the user and offer to fix before retrying.
847
- 5. The class is active immediately; no separate activation step, and no Java/Bazel-side change is needed for it to take effect — the framework discovers it automatically (§3.9) on the very next API call.
848
-
849
- After any of these, always confirm the deployment actually changed behavior by re-calling the relevant endpoint from §4.4/§5.4 and comparing the response to what was expected from the new Apex logic — a successful deploy does not by itself prove the customization is wired correctly (e.g. wrong interface name, or more than one class implementing the same interface per §3.9 rule 5, would deploy cleanly but fail or no-op at request time).
850
-
851
- ---
852
-
853
- # Part 4 — TMF621 Trouble Ticket Management (API-specific)
854
-
855
- ## 4.1 Endpoint reference (complete)
856
-
857
- TMF resource version segment: **`v5`** (this is the only version wired to a live handler; earlier `v4`/legacy paths have no handler mapping and will error).
858
-
859
- | # | Method | Path | Purpose | Query / body params |
860
- |---|---|---|---|---|
861
- | 1 | POST | `/connect/comms/troubleticket/v5/troubleticket` | Create a new trouble ticket | Body = TroubleTicket JSON (§4.2) |
862
- | 2 | GET | `/connect/comms/troubleticket/v5/troubleticket` | List trouble tickets (paginated) | `offset` (int), `pageLimit` (int), `fields` (comma list), `sort` (string, e.g. `name`, `-name`), `pageinfo` (bool — return `totalPageSize`/`currentPage`), plus any dynamic filter query params. Pagination bounds/defaults: §2.2. |
863
- | 3 | GET | `/connect/comms/troubleticket/v5/troubleticket/{id}` | Retrieve a single trouble ticket | `fields` (comma list to limit returned fields) |
864
- | 4 | PATCH | `/connect/comms/troubleticket/v5/troubleticket/{id}` | Partially update a trouble ticket | Body = partial TroubleTicket JSON — only fields present are updated |
865
- | 5 | DELETE | `/connect/comms/troubleticket/v5/troubleticket/{id}` | Delete a trouble ticket | none |
866
- | 6 | GET | `/connect/comms/troubleticket/v5/troubleticket/{id}/{subResource}` | Paginate a child collection of one ticket (TMF621 §4.6 nested-resource pattern) | `offset`, `pageLimit`, `pageinfo`; `{subResource}` = one of the 8 values in §4.3 |
867
-
868
- **`{id}` is the `CaseNumber`, not the raw Salesforce `Case` Id.** For endpoints
869
- 3–6 the path parameter is the human-readable ticket number (e.g. `00001002`),
870
- which the GET/PATCH/DELETE handlers resolve to the internal Salesforce Id via a
871
- `CaseNumber` lookup. Passing a raw `500…` `Case` Id in the URL will not match.
872
-
873
- (Every request always runs through the extensible pipeline — see §2.1.)
874
-
875
- ## 4.2 Request/response payload shape (TroubleTicket)
876
-
877
- **Request body fields (all optional unless required by validators — see §3.6):**
878
-
879
- | Field | Type | Notes |
880
- |---|---|---|
881
- | `name` | string | Ticket subject/name |
882
- | `description` | string | Free text |
883
- | `status` | string | e.g. `New`, `InProgress`, `Resolved`, `Closed` — maps to Case Status |
884
- | `ticketType` | string | TMF ticket type |
885
- | `priority` | string | e.g. `High`/`Medium`/`Low` |
886
- | `severity` | string | Comes from `CaseTroubleTicket.Severity` extension field, not `Case` directly. On POST, the `CaseTroubleTicket` extension record (which backs `severity`, `externalIdentifier`, `requestedResolutionDate`, `expectedResolutionDate`) is created **only if `severity` is set**; if `severity` is omitted, those other extension fields are dropped even when supplied |
887
- | `channel` | object | On create, only `channel.name` is read and written to `Case.Origin`; any `channel.id`/`referredType` sent in the body is ignored |
888
- | `expectedResolutionDate` | ISO date string | |
889
- | `requestedResolutionDate` | ISO date string | |
890
- | `relatedParty` | wrapper: `{ "list": [ { "partyOrPartyRole": { "id": "...", "referredType": "Account"\|"Contact" } } ] }` | Related Account/Contact references — but `Case` has only one `AccountId` and one `ContactId`, so at most one Account and one Contact persist |
891
- | `relatedEntity` | wrapper: `{ "list": [ { "entity": { "id": "...", "referredType": "Asset" } } ] }` | Related Asset references — `Case` has only one `AssetId`, so at most one Asset persists |
892
- | `note` | wrapper: `{ "list": [ { "text": "..." } ] }` | Notes to attach — each becomes a `CaseComment` (`text` → `CommentBody`) |
893
- | `attachment` | wrapper: `{ "list": [ { "url": "...", "name": "...", "description": "..." } ] }` | Files to attach — each creates a `ContentVersion` + `ContentDocumentLink` |
894
- | `troubleTicketRelationship` | wrapper: `{ "list": [ { "id": "..." } ] }` | Parent-ticket link — **only the first list entry** is used (its `id` → `Case.ParentId`) |
895
- | `externalIdentifier` | wrapper: `{ "list": [ { "id": "..." } ] }` | External system ID — **only the first list entry** is used (its `id` → `CaseTroubleTicket.ExternalId`) |
896
-
897
- **Example POST body:**
898
-
899
- ```json
900
- {
901
- "name": "VoIP call drops",
902
- "description": "Customer reports repeated dropped calls on VoIP line",
903
- "status": "New",
904
- "ticketType": "Service Issue",
905
- "priority": "High",
906
- "severity": "Critical",
907
- "relatedParty": {
908
- "list": [
909
- { "partyOrPartyRole": { "id": "001xx000003GYrsAAG", "referredType": "Account" } }
910
- ]
911
- },
912
- "relatedEntity": {
913
- "list": [
914
- { "entity": { "id": "02ixx0000004CvzAAE", "referredType": "Asset" } }
915
- ]
916
- }
917
- }
918
- ```
919
-
920
- **Response body fields** — superset of the
921
- request fields, plus: `id`, `href`, `creationDate`, `lastUpdate`,
922
- `statusChangeDate`, `resolutionDate`, `statusChangeHistory` (list),
923
- `updatedPayload` (only present if Apex extensibility overwrote the response —
924
- see §3.3). List endpoints wrap results as
925
- `{ "recordList": [ ... ], "totalPageSize": N, "currentPage": N }`.
926
-
927
- **Response quirks to be aware of:**
928
- - `id`, `@type`, and `href` are **always** returned, even when a `fields=`
929
- filter is supplied that omits them — they are hard-coded as mandatory.
930
- - `channel.id` is a **hard-coded literal `"1"`** in every response — it is not a
931
- real Salesforce Id. Only `channel.name` (from `Case.Origin`) carries real data.
932
- - Note `author` is the raw Salesforce **`CreatedById`** (a user Id), not a
933
- display name.
934
- - Attachment `mimeType` is a **hard-coded literal string `"MimeType"`**, not the
935
- real file MIME type — do not rely on it.
936
- - `statusChangeHistory` is **read-only**: it is derived from `Case.Histories`
937
- filtered to `Field == 'Status'`, each entry has `@type == "StatusChange"`, and
938
- it cannot be set via POST/PATCH.
939
-
940
- ## 4.3 Sub-resource pagination (endpoint #6)
941
-
942
- `GET /connect/comms/troubleticket/v5/troubleticket/{id}/{subResource}` implements
943
- TMF621 §4.6's nested-collection pattern. Supported built-in `{subResource}` values
944
- (case-insensitive in the URL, matched lowercase internally):
945
-
946
- | `{subResource}` value | Backing relationship | Maps to |
947
- |---|---|---|
948
- | `note` | `Case.CaseComments` | `CaseComment` |
949
- | `attachment` | `Case.ContentDocumentLinks` | `ContentDocumentLink` |
950
- | `statuschangehistory` | `Case.Histories` | `CaseHistory` |
951
- | `relatedentity` | `Asset` reference | `Asset` |
952
- | `troubleticketrelationship` | `Case.Parent` | Parent `Case` |
953
- | `relatedparty` | Combined Account + Contact references | (special-cased, not a plain relationship) |
954
- | `channel` | `Case.Origin` | (special-cased scalar field; `channel.id` is always the literal `"1"`, `channel.name` = `Origin`) |
955
- | `externalidentifier` | `CaseTroubleTicket` | (requires an auxiliary query) |
956
-
957
- Response shape matches the list endpoint: `{ "recordList": [...], "totalPageSize": N, "currentPage": N }`.
958
- Apex extensibility (via `transformRequest`, see Part 3) can register **additional,
959
- completely custom sub-resource names** at runtime that aren't in this table —
960
- see §3.7.
961
-
962
- ## 4.4 API routes — TMF621, zero customization
963
-
964
- Call these directly against `https://<your-instance>.my.salesforce.com/services/data/v<API_VERSION>.0<path>` per Part 1. No Apex required for any of these.
965
-
966
- | Purpose | Method | Path | Body |
967
- |---|---|---|---|
968
- | List (first page, 10 per page) | GET | `/connect/comms/troubleticket/v5/troubleticket?offset=0&pageLimit=10&pageinfo=true` | — |
969
- | Create | POST | `/connect/comms/troubleticket/v5/troubleticket` | `{"name":"VoIP call drops","description":"Repeated drops","status":"New","priority":"High"}` |
970
- | Retrieve by id | GET | `/connect/comms/troubleticket/v5/troubleticket/00001002` | — |
971
- | Partial update | PATCH | `/connect/comms/troubleticket/v5/troubleticket/00001002` | `{"status":"Resolved"}` |
972
- | Delete | DELETE | `/connect/comms/troubleticket/v5/troubleticket/00001002` | — |
973
- | Sub-resource: paginate notes on a ticket | GET | `/connect/comms/troubleticket/v5/troubleticket/00001002/note?offset=0&pageLimit=5` | — |
974
-
975
- (`{id}` in these routes is the `CaseNumber`, e.g. `00001002` — see §4.1.)
976
-
977
- **Troubleshooting a failing GET:** if a TMF621 GET (list, retrieve-by-id, or a
978
- sub-resource page) fails, first **show the user the actual error text from the
979
- response body verbatim** — do not paraphrase or guess at the cause. Then, if the
980
- failure looks like an access/field error, ask the user to **enable FLS
981
- read/edit on the calling user's profile or permission set for all `Case` fields**
982
- (this API is a Connect resource, so ordinary field-level security applies on top
983
- of the TMF access check in §1.1 — a field the caller can't read will break the
984
- underlying query). Confirm the FLS change and re-run the same route before
985
- investigating anything else.
986
-
987
- ## 4.5 Filtering and sorting fields — TMF621
988
-
989
- See §2.3 for how filtering/sorting works generically. The named TMF-field
990
- mapping below — these TMF names work for filtering, sorting, and `fields=`
991
- selection; any other raw Salesforce `Case` field name (e.g. `Origin`,
992
- `AccountId`, `ClosedDate`) also works for filtering/sorting even though it has no
993
- TMF alias (filtering allows any valid Salesforce field, not just the aliases
994
- below).
995
-
996
- **If a user wants to filter by a field not in the table below, follow the
997
- decision flow in §2.3.1** (confirm it's on `Case` → raw param, no extensibility;
998
- custom name/related object → extensibility with a mapping; not on the entity →
999
- say it can't be done). And note the empty-result gotcha from §2.3: a filter on a
1000
- non-existent field returns an **empty list, not an error**.
1001
-
1002
- **Supported filter fields (TMF name → Salesforce field):**
1003
-
1004
- | TMF name | Salesforce field |
1005
- |---|---|
1006
- | `name` | `Subject` |
1007
- | `id` | `CaseNumber` |
1008
- | `description` | `Description` |
1009
- | `status` | `Status` |
1010
- | `tickettype` | `Type` |
1011
- | `priority` | `Priority` |
1012
-
1013
- Plus any raw Salesforce `Case` field name passed directly as a query param.
1014
-
1015
- **Not supported for filtering:**
1016
- - `t` — a Workbench artifact param, explicitly stripped.
1017
- - Any filter with a `null` field name or `null` value is silently dropped.
1018
-
1019
- **Supported sort fields (`sort=` query param, minVersion 264):**
1020
- - The same flat mapping as filtering: `name`, `id`, `description`, `status`,
1021
- `tickettype`, `priority`, plus any raw Salesforce `Case` field name.
1022
- - Nested (dotted) 1:1-relationship sort paths, explicitly whitelisted:
1023
-
1024
- | TMF dotted sort path | Salesforce path |
1025
- |---|---|
1026
- | `relatedEntity.name` | `Asset.Name` |
1027
- | `troubleTicketRelationship.id` | `Parent.CaseNumber` |
1028
- | `troubleTicketRelationship.name` | `Parent.Subject` |
1029
-
1030
- **Not supported for sorting:** multi-record/child connections —
1031
- `note`, `attachment`, `statusChangeHistory` — since these are not sortable via
1032
- the parent list.
1033
-
1034
- ### Full TMF621 field mapping reference — use this when writing extensibility hooks
1035
-
1036
- Primary backing SObject is `Case` (+ `CaseTroubleTicket` for `severity`). Use
1037
- these mappings inside `transformRequest`/`customiseGraphQLQuery`/`handlePostOperation`
1038
- when you need to translate between a TMF field/sub-resource name and the
1039
- underlying Salesforce field, or vice versa.
1040
-
1041
- **1. Flat TMF name → Salesforce field (filtering, sorting, `fields=`):**
1042
-
1043
- | TMF field | Salesforce field |
1044
- |---|---|
1045
- | `name` | `Subject` |
1046
- | `id` | `CaseNumber` |
1047
- | `description` | `Description` |
1048
- | `status` | `Status` |
1049
- | `tickettype` | `Type` |
1050
- | `priority` | `Priority` |
1051
-
1052
- **2. `severity` — special case, not in the flat mapping table (table 1):**
1053
-
1054
- | TMF field | Salesforce entity.field |
1055
- |---|---|
1056
- | `severity` | `CaseTroubleTicket.Severity` — fetched via an auxiliary GraphQL query, not from `Case` directly |
1057
-
1058
- **3. TMF sub-resource name → identifier → backing relationship:**
1059
-
1060
- | TMF sub-resource | Identifier | Backing relationship |
1061
- |---|---|---|
1062
- | `channel` | `channel` | `Case.Origin` (scalar) |
1063
- | `attachment` | `attachment` | `Case.ContentDocumentLinks` |
1064
- | `note` | `note` | `Case.CaseComments` |
1065
- | `relatedEntity` | `relatedEntity` | `Asset` reference |
1066
- | `relatedParty` | `relatedParty` | Account + Contact references (special-cased) |
1067
- | `troubleTicketRelationship` | `troubleTicketRelationship` | `Case.Parent` |
1068
- | `externalIdentifier` | `externalIdentifier` | `CaseTroubleTicket` (auxiliary query) |
1069
- | `statusChangeHistory` | `statusChangeHistory` | `Case.Histories` |
1070
-
1071
- **4. Dotted sort path → Salesforce relationship path:**
1072
-
1073
- | TMF dotted path | Salesforce path |
1074
- |---|---|
1075
- | `relatedentity.name` | `Asset.Name` |
1076
- | `troubleticketrelationship.id` | `Parent.CaseNumber` |
1077
- | `troubleticketrelationship.name` | `Parent.Subject` |
1078
-
1079
- **5. Salesforce fields returned when the caller omits `fields=`:**
1080
-
1081
- `Subject`, `Id`, `Description`, `Status`, `Type`, `Priority`, `Origin`,
1082
- `AccountId`, `ContactId`, `CaseNumber`, `CreatedDate`, `LastModifiedDate`,
1083
- `ClosedDate`, `ParentId`, `AssetId`.
1084
-
1085
- Any raw Salesforce `Case` field not listed in table 1 also works for
1086
- filtering/sorting/`fields=` — these maps are TMF-friendly aliases, not a
1087
- restriction (§2.3).
1088
-
1089
- ## 4.6 PATCH update semantics — TMF621
1090
-
1091
- PATCH is partial: **a field omitted from the request body is left unchanged.**
1092
- Beyond that, per-field behavior differs:
1093
-
1094
- | TMF field | Salesforce target | PATCH behavior |
1095
- |---|---|---|
1096
- | `name`, `description`, `status`, `ticketType`, `priority` | `Case.Subject`/`Description`/`Status`/`Type`/`Priority` | Set to the new value if present. |
1097
- | `channel` | `Case.Origin` | Set to `channel.name` if `channel` present and `name` non-empty. |
1098
- | `relatedParty` | `Case.AccountId`, `Case.ContactId` | **Replace.** First Account in list → `AccountId`, first Contact → `ContactId`. Empty list `[]` → **clears both to null**. Omitted → no change. |
1099
- | `relatedEntity` | `Case.AssetId` | **Replace.** First Asset → `AssetId`. Empty list `[]` → **clears to null**. Omitted → no change. |
1100
- | `troubleTicketRelationship` | `Case.ParentId` | First item `id` → `ParentId` when list is non-empty. |
1101
- | `note` | `CaseComment` | **Append-only.** Each entry with non-empty `text` creates a new `CaseComment`; existing notes are never removed. Notes are always created via SOQL, not GraphQL. |
1102
- | `attachment` | `ContentVersion` + `ContentDocumentLink` | **Append-only.** Each entry with non-empty `url` creates a new attachment; existing attachments are never removed. |
1103
- | `severity`, `externalIdentifier`, `requestedResolutionDate`, `expectedResolutionDate` | `CaseTroubleTicket` extension | **Update-or-create.** If a `CaseTroubleTicket` already exists for the Case → update it; otherwise create one. Dates are converted to ISO-8601 DateTime. |
1104
-
1105
- The `{id}` in the PATCH URL is the `CaseNumber`, resolved to the Salesforce Case
1106
- Id before the mutation runs (§4.1).
1107
-
1108
- ---
1109
-
1110
- # Part 5 — TMF629 Customer Management (API-specific)
1111
-
1112
- ## 5.1 Endpoint reference (complete)
1113
-
1114
- TMF resource version segment: **`v4`** — this is the only version wired to the current extensible handler path for every verb below. (A `v6` segment exists in the handler routing table for GET, but only keyed to the deprecated, non-extensible legacy resource capped at Salesforce API version 258, not to the current extensible resource — it is not covered by any functional test and should not be used. Always use `v4`.)
1115
-
1116
- | # | Method | Path | Purpose | Query / body params |
1117
- |---|---|---|---|---|
1118
- | 1 | POST | `/connect/comms/customermanagement/v4/customer` | Create a customer (creates an `Account`, and optionally a related `Contact` as `engagedParty`) | Body = Customer JSON (§5.2) |
1119
- | 2 | GET | `/connect/comms/customermanagement/v4/customer` | List customers (paginated) | `offset`, `pageLimit`, `fields`, `sort`, `pageinfo`, plus dynamic filter query params. Pagination bounds/defaults: §2.2. |
1120
- | 3 | GET | `/connect/comms/customermanagement/v4/customer/{id}` | Retrieve a single customer by `Account` Id | `fields` |
1121
- | 4 | PATCH | `/connect/comms/customermanagement/v4/customer/{id}` | Partially update a customer | Body = partial Customer JSON |
1122
- | 5 | DELETE | `/connect/comms/customermanagement/v4/customer/{id}` | Delete a customer (`Account`) | none |
1123
- | 6 | GET | `/connect/comms/customermanagement/v4/customer/{id}/{subResource}` | Paginate a child collection (TMF630 §4.6 nested-resource pattern — TMF629 borrows TMF630's generic customer sub-resource convention) | `offset`, `pageLimit`, `pageinfo`; `{subResource}` = one of the 3 values in §5.3 |
1124
-
1125
- (Every request always runs through the extensible pipeline — see §2.1.)
1126
-
1127
- ## 5.2 Request/response payload shape (Customer)
1128
-
1129
- **Request body fields:**
1130
-
1131
- | Field | Type | Notes |
1132
- |---|---|---|
1133
- | `name` | string | Customer/Account name |
1134
- | `status` | string | Account status |
1135
- | `type` | string | Customer type |
1136
- | `engagedParty` | object: `{ "referredType": "Contact"\|"Individual", "name": "...", "id": null }` | The primary contact person. **Mandatory on POST** (POST requires both `name` and `engagedParty`); the object itself must carry a `name` or an `id`. On POST **without** `id`, a new `Contact` is created from `name` → `Contact.LastName`; on POST **with** `id`, the existing `Contact` is linked/updated. |
1137
- | `contactMedium` | wrapper: `{ "list": [ { "mediumType": "...", "characteristic": { ... } } ] }` | Phone/email/fax contact points. **Only `Email`, `Phone`, `Fax` mediumTypes are honored** — any other value is silently ignored. If the **same mediumType appears more than once**, only one value persists (backed by a map keyed on the Salesforce field — last write wins). Each entry needs a non-blank `mediumType` and `characteristic` or the request fails (`contactMediumValidation`). |
1138
- | `account` | wrapper: `{ "list": [ { "id": "...", "name": "..." } ] }` | Related/child account references. Each entry needs **both `id` and `name`** (`accountValidation`), and every referenced account **must already exist** — matched via `Id CONTAINS` + `Name CONTAINS` against existing `Account`s; any that don't match fail the request with `ACCOUNT_NOT_PRESENT`. |
1139
-
1140
- **Example POST body:**
1141
-
1142
- ```json
1143
- {
1144
- "name": "Sample",
1145
- "type": "Enterprise",
1146
- "engagedParty": {
1147
- "referredType": "Contact",
1148
- "name": "Sean Forbes",
1149
- "id": null
1150
- }
1151
- }
1152
- ```
1153
-
1154
- **Response body fields**: `id`, `href`,
1155
- `type`, `name`, `agreement` (list of related `Contract`s), `account` (list of
1156
- related child `Account`s), `engagedParty` (single Account-Contact relation
1157
- object), `contactMedium` (list), `updatedPayload` (present only if Apex
1158
- extensibility overwrote the response — see §3.3). The `engagedParty.role` in the
1159
- response defaults to the literal `"Individual"` when there is no
1160
- `AccountContactRelation` role to draw from.
1161
-
1162
- ## 5.3 Sub-resource pagination (endpoint #6)
1163
-
1164
- `GET /connect/comms/customermanagement/v4/customer/{id}/{subResource}`. Built-in values:
1165
-
1166
- | `{subResource}` value | Backing relationship | Maps to |
1167
- |---|---|---|
1168
- | `account` | `Account.ChildAccounts` | Child `Account` |
1169
- | `engagedparty` (alias: `contact`) | `Account.Contacts` | `Contact` |
1170
- | `agreement` | `Account.Contracts` | `Contract` |
1171
-
1172
- Same response shape as the list endpoint. As with TMF621, Apex extensibility
1173
- can register additional custom sub-resource names at runtime (§3.7) — for
1174
- example, exposing `Account.Opportunities` as a brand-new `opportunities`
1175
- sub-resource this way, with zero Java changes.
1176
-
1177
- ## 5.4 API routes — TMF629, zero customization
1178
-
1179
- Call these directly against `https://<your-instance>.my.salesforce.com/services/data/v<API_VERSION>.0<path>` per Part 1. No Apex required for any of these.
1180
-
1181
- | Purpose | Method | Path | Body |
1182
- |---|---|---|---|
1183
- | List | GET | `/connect/comms/customermanagement/v4/customer?offset=0&pageLimit=10&pageinfo=true` | — |
1184
- | Create | POST | `/connect/comms/customermanagement/v4/customer` | `{"name":"Sample","type":"Enterprise","engagedParty":{"referredType":"Contact","name":"Sean Forbes","id":null}}` |
1185
- | Retrieve by id | GET | `/connect/comms/customermanagement/v4/customer/001xx000003GYrsAAG` | — |
1186
- | Partial update | PATCH | `/connect/comms/customermanagement/v4/customer/001xx000003GYrsAAG` | `{"status":"Active"}` |
1187
- | Delete | DELETE | `/connect/comms/customermanagement/v4/customer/001xx000003GYrsAAG` | — |
1188
- | Sub-resource: paginate related contacts (engagedParty) | GET | `/connect/comms/customermanagement/v4/customer/001xx000003GYrsAAG/engagedparty?offset=0&pageLimit=5` | — |
1189
-
1190
- ## 5.5 Filtering and sorting fields — TMF629
1191
-
1192
- Same mechanism as TMF621 (§2.3 / §4.5): filtering is not a fixed allow-list.
1193
- TMF629 uses the same filter field-name pass-through as TMF621 — the underlying
1194
- filter logic drops the field name straight into the `where` clause with no
1195
- allow-list check (only `t`/null skipped). The table below is the named TMF-field
1196
- mapping (TMF name → Salesforce field); any other raw Salesforce `Account` field
1197
- name also works for filtering/sorting.
1198
-
1199
- **If a user wants to filter by a field not in the table below, follow the
1200
- decision flow in §2.3.1** (confirm it's on `Account` → raw param, no
1201
- extensibility; custom name/related object → extensibility with a mapping; not on
1202
- the entity → say it can't be done). Same empty-result gotcha as §2.3: a filter on
1203
- a non-existent field returns an **empty list, not an error**.
1204
-
1205
- **Supported filter fields (TMF name → Salesforce field):**
1206
-
1207
- | TMF name | Salesforce field |
1208
- |---|---|
1209
- | `name` | `Name` |
1210
- | `id` | `Id` |
1211
-
1212
- Plus any raw Salesforce `Account` field name passed directly as a query param.
1213
-
1214
- **Not supported for filtering:**
1215
- - `t` — same Workbench artifact param, explicitly stripped.
1216
- - Any filter with a `null` field name or `null` value is silently dropped.
1217
-
1218
- **Supported sort fields (`sort=` query param, minVersion 264):**
1219
- - `name`, `id`, plus any raw Salesforce `Account` field name.
1220
- - One nested (dotted) sort path is whitelisted:
1221
-
1222
- | TMF dotted sort path | Salesforce path |
1223
- |---|---|
1224
- | `parentAccount.name` | `Parent.Name` |
1225
-
1226
- **Not supported for sorting:** multi-record sub-resources — `account` (child
1227
- accounts), `agreement` (contracts), `contactMedium`, `engagedParty` — these are
1228
- GraphQL connections and, like TMF621's `note`/`attachment`/`statusChangeHistory`,
1229
- are not sortable via the parent list.
1230
-
1231
- ### Full TMF629 field mapping reference — use this when writing extensibility hooks
1232
-
1233
- Primary backing SObject is `Account`. Use these mappings inside
1234
- `transformRequest`/`customiseGraphQLQuery`/`handlePostOperation` when you need to
1235
- translate between a TMF field/sub-resource name and the underlying Salesforce
1236
- field, or vice versa.
1237
-
1238
- **1. Flat TMF name → Salesforce field (filtering, sorting, `fields=`):**
1239
-
1240
- | TMF field | Salesforce field |
1241
- |---|---|
1242
- | `name` | `Name` |
1243
- | `id` | `Id` |
1244
-
1245
- **2. TMF sub-resource name → identifier:**
1246
-
1247
- | TMF sub-resource | Identifier |
1248
- |---|---|
1249
- | `account` | `account` |
1250
- | `agreement` | `agreement` |
1251
- | `contactmedium` | `contactmedium` |
1252
- | `engagedparty` | `engagedparty` |
1253
-
1254
- **3. Dotted sort path → Salesforce relationship path:**
1255
-
1256
- | TMF dotted path | Salesforce path |
1257
- |---|---|
1258
- | `parentaccount.name` | `Parent.Name` |
1259
-
1260
- **4. Salesforce fields returned when caller omits `fields=`:**
1261
-
1262
- `Name`, `Id`, `ParentId`.
1263
-
1264
- **5. TMF sub-resource → GraphQL relationship → backing SObject → parent lookup field, used for `GET .../{id}/{subResource}`:**
1265
-
1266
- | TMF sub-resource | GraphQL relationship (alias) | Backing SObject | Parent lookup field |
1267
- |---|---|---|---|
1268
- | `account` | `ChildAccounts` (`childAccounts`) | `Account` | `ParentId` |
1269
- | `engagedparty` (alias: `contact`) | `Contacts` (`engagedParty`) | `Contact` | `AccountId` |
1270
- | `agreement` | `Contracts` (`agreements`) | `Contract` | `AccountId` |
1271
-
1272
- **6. Parent-side relationship node (feeds the `account` array field alongside `ChildAccounts`):**
1273
-
1274
- | GraphQL relationship (alias) | Backing SObject | Purpose |
1275
- |---|---|---|
1276
- | `Parent` (`parentAccount`) | `Account` | Direct (non-connection) relationship; parent + child records both populate the TMF `account` array field |
1277
-
1278
- **7. `contactmedium` and `engagedparty` share the same underlying GraphQL node:**
1279
-
1280
- Both pull from the same `Contacts` relationship (alias `engagedParty`), just
1281
- shaped differently on the way out:
1282
- - If `contactmedium` was requested → contact records convert into the TMF
1283
- `contactMedium` field.
1284
- - If `engagedparty` was requested → the first contact record converts into the
1285
- TMF `engagedParty` field.
1286
- - Both can be requested together — they populate independently from the same
1287
- underlying `Contact` rows.
1288
-
1289
- **8. `contactMedium.mediumType` → Salesforce field (used when constructing the mutation payload on POST):**
1290
-
1291
- | TMF `mediumType` | Salesforce field |
1292
- |---|---|
1293
- | `PHONE` | `Phone` |
1294
- | `EMAIL` | `Email` |
1295
- | `FAX` | `Fax` |
1296
-
1297
- **9. `account` wrapper (POST request body) — construction/match logic:**
1298
-
1299
- | TMF field | Salesforce entity | Match logic |
1300
- |---|---|---|
1301
- | `account.list[].id` / `.name` | `Account` | Matched via `Id CONTAINS` + `Name CONTAINS` filters against existing `Account` records — every referenced account must already exist or the request fails with `ACCOUNT_NOT_PRESENT` |
1302
-
1303
- Any raw Salesforce `Account` field not listed in table 1 also works for
1304
- filtering/sorting/`fields=` — these maps are TMF-friendly aliases, not a
1305
- restriction (§2.3).
1306
-
1307
- **Sub-resource pagination — no `sort=`/`fields=` support:** `GET .../{id}/{subResource}`
1308
- only accepts `offset`, `pageLimit`, `pageinfo` — there is no caller-facing sort
1309
- or field-selection parameter on this endpoint. Default order is unsorted/Id-order unless an Apex
1310
- `customiseGraphQLQuery` hook injects a `where`/`orderBy` argument onto the
1311
- specific sub-resource's GraphQL node (matched by alias, e.g. `childAccounts`,
1312
- `agreements`, `engagedParty`) — this is the *only* way to sort or filter a
1313
- sub-resource collection.
1314
-
1315
- ## 5.6 PATCH update semantics — TMF629
1316
-
1317
- PATCH is partial: a field omitted from the body is left unchanged. Patchable
1318
- fields and their behavior:
1319
-
1320
- | TMF field | Salesforce target | PATCH behavior |
1321
- |---|---|---|
1322
- | `name` | `Account.Name` | Set to the new value if present. |
1323
- | `account` | `Account` (child links) | Each entry needs `id`+`name` and must match an existing Account (same `CONTAINS` rule / `ACCOUNT_NOT_PRESENT` failure as POST). |
1324
- | `contactMedium` | `Contact.Email`/`Phone`/`Fax` | Applied to the engaged-party contact. Only `Email`/`Phone`/`Fax` honored; a `characteristic: {}` for a mediumType effectively clears that medium. |
1325
- | `engagedParty` | `Contact` link | **Supported on PATCH**, but **`engagedParty.id` is required**. The currently-linked contact(s) are delinked when the new `id` differs, then the contact identified by `id` is linked/updated. |
1326
-
1327
- **Note:** `engagedParty` **is supported on PATCH** as described above, despite
1328
- older documentation that may suggest otherwise.
1329
-
1330
- ---
1331
-
1332
- # Part 6 — End-to-end execution checklist
1333
-
1334
- 1. **Confirm you hold a valid OAuth/session bearer token for the target org.** Check that the token is live and not expired. If you already have one, skip step 2 and go straight to step 3.
1335
- 2. **If not, follow the bundled reference sample `external-client-app-setup` in the skill's references bundle, per §1.2.** In short: first list the org's existing External Client Apps via the Metadata API and reuse a suitable one; only create a new app if none exists — using the defaults unless the user asks to customize, applying the default configuration incl. the Client Credentials run-as user, having the user reveal and store the Consumer Key/Secret in the approved secret manager, then obtaining an access token via a standard OAuth 2.0 flow (web-server/user-agent for interactive use, Client Credentials with the run-as user, or JWT bearer via `POST /services/oauth2/token` for headless/CI). Do not proceed to step 3 until you hold a live token for the target org.
1336
- 3. Confirm your org has `CommsCloud` or `CommsCloudTMFAPI` org permission enabled (ask a Salesforce admin, or check Setup → Feature Settings if you have access).
1337
- 4. **Discover the org's latest supported Salesforce API version.** Call `GET https://<your-instance>.my.salesforce.com/services/data/` with the `Bearer` token and take the highest `version` from the response; use it as the `v<API_VERSION>.0` segment in the base URL (per §1.3). A `200` confirms auth and the correct version to use; never hard-code a version the org may not report.
1338
- 5. Pick your TMF resource version segment: TMF621 → `v5`; TMF629 → `v4` (this is separate from the Salesforce API version from step 4).
1339
- 6. **Verify the endpoint is actually accessible in the org** before doing any real work: call the plain list/GET endpoint (§4.4 #1 or §5.4 #1) with `pageLimit=1` and no filters. A `200` with a (possibly empty) `recordList` confirms the org permission (§1.1), the access check, and FLS/sharing all line up. A `403`/`INSUFFICIENT_ACCESS` means the org permission (`CommsCloud`/`CommsCloudTMFAPI`) isn't enabled or the user lacks object access — stop and fix that before proceeding. A `404` means the wrong TMF version segment was used (double-check step 5).
1340
- 7. Call the endpoint route directly using the tables in §4.4/§5.4 — this works out of the box, zero Apex needed.
1341
- 8. **Only if you explicitly want custom behavior** (extra fields, filtering, validation, response reshaping) — per the mandatory gate at the top of this skill: **first confirm the org's latest supported API version (step 4) is `67.0` or above — extensibility does not work below `67.0`; if the org is below it, stop and tell the user rather than writing any Apex.** Then confirm you actually want an extensibility scenario, pick the one interface from §3.1 matching your verb+API, write one Apex class implementing every method on it (no-op the ones you don't need per §3.2's "No-op return" column), show it and get it confirmed, deploy it per §3.11, and call the same endpoint again — your customization takes effect automatically with no further wiring.
1342
-
1343
- <!-- skill-validate: ignore-start -->
1344
- <!-- codey:operations start -->
1345
- ## Operations Reference
1346
-
1347
- Operations this skill uses — routing and dependency reference.
1348
-
1349
- | Operation | Purpose | Status | Call | Depends on |
1350
- |-----------|---------|--------|------|------------|
1351
- | `tmf-list-troubletickets` | read | — | `GET /services/data/v{V}/connect/comms/troubleticket/v5/troubleticket` | — |
1352
- | `tmf-get-troubleticket` | read | — | `GET /services/data/v{V}/connect/comms/troubleticket/v5/troubleticket/{caseNumber}` | `tmf-list-troubletickets` |
1353
- | `tmf-get-troubleticket-subresource` | read | — | `GET /services/data/v{V}/connect/comms/troubleticket/v5/troubleticket/{caseId}/{subresource}` | `tmf-resolve-case-id` |
1354
- | `tmf-resolve-case-id` | read | — | `POST /services/data/v{V}/graphql` | — |
1355
- | `tmf-create-troubleticket` | write | — | `POST /services/data/v{V}/connect/comms/troubleticket/v5/troubleticket` | — |
1356
- | `tmf-update-troubleticket` | write | — | `PATCH /services/data/v{V}/connect/comms/troubleticket/v5/troubleticket/{caseNumber}` | `tmf-get-troubleticket` |
1357
- | `tmf-delete-troubleticket` | write | — | `DELETE /services/data/v{V}/connect/comms/troubleticket/v5/troubleticket/{caseNumber}` | — |
1358
- | `cust-list-customers` | read | — | `GET /services/data/v{V}/connect/comms/customermanagement/v4/customer` | — |
1359
- | `cust-create-customer` | write | — | `POST /services/data/v{V}/connect/comms/customermanagement/v4/customer` | — |
1360
- | `cust-get-customer` | read | — | `GET /services/data/v{V}/connect/comms/customermanagement/v4/customer/{accountId}` | `cust-list-customers` |
1361
- | `cust-patch-customer` | write | — | `PATCH /services/data/v{V}/connect/comms/customermanagement/v4/customer/{accountId}` | `cust-get-customer` |
1362
- | `cust-get-customer-subresource` | read | — | `GET /services/data/v{V}/connect/comms/customermanagement/v4/customer/{accountId}/{subresource}` | `cust-list-customers` |
1363
- | `cust-delete-customer` | write | — | `DELETE /services/data/v{V}/connect/comms/customermanagement/v4/customer/{accountId}` | — |
1364
- | `apex-deploy-toolingapi` | write | — | `POST /services/data/v{V}/tooling/sobjects/ApexClass` | — |
1365
- | `apex-deploy-metadataapi` | write | — | `/headless/metadata` | — |
1366
- | `apex-conflict-check` | — | — | — | — |
1367
- <!-- codey:operations end -->
1368
- <!-- skill-validate: ignore-end -->