@salesforce/afv-skills 1.41.0 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -6
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +3 -0
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +4 -0
- package/skills/design-systems-slds-validate/SKILL.md +2 -2
- package/skills/dx-app-analytics-query/SKILL.md +1 -3
- package/skills/dx-devops-conflict-resolve/SKILL.md +205 -0
- package/skills/dx-devops-conflict-resolve/examples/conflict-workflows.md +165 -0
- package/skills/dx-devops-conflict-resolve/references/deploy-failure-resolution.md +75 -0
- package/skills/dx-devops-conflict-resolve/references/git-conflict-resolution.md +124 -0
- package/skills/dx-devops-conflict-resolve/scripts/detect-conflicts.sh +91 -0
- package/skills/dx-devops-conflict-resolve/scripts/diagnose-deploy-failure.sh +156 -0
- package/skills/dx-devops-request-status/SKILL.md +160 -0
- package/skills/dx-devops-request-status/examples/polling-workflows.md +101 -0
- package/skills/dx-devops-request-status/references/cli-commands.md +176 -0
- package/skills/dx-devops-request-status/scripts/poll-status.sh +134 -0
- package/skills/dx-devops-test-failures-analyze/SKILL.md +6 -6
- package/skills/dx-devops-test-pipeline-configure/SKILL.md +7 -7
- package/skills/dx-devops-test-suite-assignments-configure/SKILL.md +6 -6
- package/skills/dx-devops-test-suite-run/SKILL.md +6 -6
- package/skills/dx-devops-work-item-manage/SKILL.md +2 -0
- package/skills/dx-org-manage/references/creating-scratch-org.md +5 -2
- package/skills/dx-org-manage/references/creating-snapshot.md +1 -0
- package/skills/dx-org-shape-manage/SKILL.md +152 -0
- package/skills/dx-org-shape-manage/examples/create_error_output.json +9 -0
- package/skills/dx-org-shape-manage/examples/create_success_output.json +9 -0
- package/skills/dx-org-shape-manage/examples/delete_output.json +11 -0
- package/skills/dx-org-shape-manage/examples/list_inactive_output.json +32 -0
- package/skills/dx-org-shape-manage/examples/list_output.json +24 -0
- package/skills/dx-org-shape-manage/references/cli_flags.md +135 -0
- package/skills/dx-org-switch/SKILL.md +2 -2
- package/skills/dx-org-trial-expiration-check/SKILL.md +2 -0
- package/skills/dx-pkg-post-install-configure/SKILL.md +3 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +211 -0
- package/skills/education-cloud-multi-campus-configure/examples/hierarchy_visualization.md +174 -0
- package/skills/education-cloud-multi-campus-configure/examples/output_examples.md +54 -0
- package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input.csv +29 -0
- package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input_edgecases.csv +16 -0
- package/skills/education-cloud-multi-campus-configure/references/account_recordtype_prerequisite.md +44 -0
- package/skills/education-cloud-multi-campus-configure/references/delta_computation.md +33 -0
- package/skills/education-cloud-multi-campus-configure/references/error_handling.md +198 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +54 -0
- package/skills/education-cloud-multi-campus-configure/references/gotchas.md +16 -0
- package/skills/education-cloud-multi-campus-configure/references/hierarchy_parsing_rules.md +133 -0
- package/skills/education-cloud-multi-campus-configure/references/mcp-invocation.md +75 -0
- package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/SKILL.md +1 -1
- package/skills/experience-cms-brand-apply/SKILL.md +4 -1
- package/skills/experience-cms-brand-create/SKILL.md +197 -0
- package/skills/experience-cms-brand-create/assets/brand-template.json +329 -0
- package/skills/experience-cms-brand-create/references/brand-anatomy.md +120 -0
- package/skills/experience-cms-brand-create/references/disk-contract.md +84 -0
- package/skills/experience-cms-content-generate/SKILL.md +190 -0
- package/skills/experience-cms-content-generate/assets/display-formats.md +84 -0
- package/skills/experience-cms-content-generate/assets/payloads/create-content-bulk.json +11 -0
- package/skills/experience-cms-content-generate/assets/payloads/create-content-single.json +11 -0
- package/skills/experience-cms-content-generate/assets/payloads/publish-content.json +4 -0
- package/skills/experience-cms-content-generate/assets/payloads/update-content.json +9 -0
- package/skills/experience-cms-content-generate/assets/questions.md +241 -0
- package/skills/experience-cms-content-generate/examples/create-content-call.md +81 -0
- package/skills/experience-cms-content-generate/references/bulk-batching.md +56 -0
- package/skills/experience-cms-content-generate/references/content-type-classification.md +88 -0
- package/skills/experience-cms-content-generate/references/content-write-tool.md +95 -0
- package/skills/experience-cms-content-generate/references/delegation-protocol.md +37 -0
- package/skills/experience-cms-content-generate/references/edit-publish-workflow.md +63 -0
- package/skills/experience-cms-content-generate/references/error-recovery.md +72 -0
- package/skills/experience-cms-content-generate/references/identifier-resolution.md +26 -0
- package/skills/experience-cms-content-generate/references/intent-routing.md +45 -0
- package/skills/experience-cms-content-generate/references/principles.md +19 -0
- package/skills/experience-cms-content-generate/references/ux-rules.md +68 -0
- package/skills/experience-cms-content-generate/references/workspace-resolution.md +33 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +449 -0
- package/skills/experience-cms-content-type-generate/assets/discovery-prompts.md +79 -0
- package/skills/experience-cms-content-type-generate/assets/schema-example.json +70 -0
- package/skills/experience-cms-content-type-generate/references/agent-checklist.md +50 -0
- package/skills/experience-cms-content-type-generate/references/anti-patterns.md +39 -0
- package/skills/experience-cms-content-type-generate/references/deployment-errors.md +27 -0
- package/skills/experience-cms-content-type-generate/references/discovery-details.md +106 -0
- package/skills/experience-cms-content-type-generate/references/discovery-query-rules.md +85 -0
- package/skills/experience-cms-content-type-generate/references/edit-fields-loop.md +48 -0
- package/skills/experience-cms-content-type-generate/references/pre-deploy-checklist.md +21 -0
- package/skills/experience-cms-content-type-generate/references/retrieve-and-reconcile.md +105 -0
- package/skills/experience-cms-content-type-generate/references/schema-rules.md +51 -0
- package/skills/experience-cms-content-type-generate/references/schema-summary-format.md +60 -0
- package/skills/experience-lds-graphql-generate/SKILL.md +4 -0
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +79 -0
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +205 -0
- package/skills/experience-lwc-design-generate/SKILL.md +5 -4
- package/skills/experience-lwc-generate/SKILL.md +10 -9
- package/skills/experience-ui-bundle-deploy/references/dev-preview.md +21 -0
- package/skills/experience-ui-bundle-deploy/references/social-login.md +1 -0
- package/skills/experience-ui-bundle-file-upload-generate/SKILL.md +6 -6
- package/skills/experience-ui-bundle-localize/SKILL.md +30 -37
- package/skills/experience-ui-bundle-localize/references/gotchas.md +23 -7
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +48 -4
- package/skills/experience-ui-bundle-localize/references/label-xml.md +22 -8
- package/skills/experience-ui-bundle-localize/references/verifying.md +39 -11
- package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +118 -15
- package/skills/experience-ui-bundle-localize/scripts/tests/test-detect-bundle-type.sh +187 -0
- package/skills/experience-ui-bundle-mfa-configure/SKILL.md +13 -2
- package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +1 -0
- package/skills/experience-ui-bundle-project-generate/SKILL.md +2 -2
- package/skills/integration-connectivity-generate/SKILL.md +11 -10
- package/skills/integration-eventing-cdc-configure/SKILL.md +6 -6
- package/skills/integration-eventing-subscription-configure/SKILL.md +6 -5
- package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +2 -2
- package/skills/mobile-platform-offline-validate/scripts/package.json +1 -1
- package/skills/platform-agentexchange-partner-offers-configure/SKILL.md +8 -8
- package/skills/platform-custom-application-generate/SKILL.md +2 -0
- package/skills/platform-custom-field-generate/SKILL.md +8 -6
- package/skills/platform-custom-metadata-type-generate/SKILL.md +460 -0
- package/skills/platform-custom-metadata-type-generate/references/cmdt-records.md +251 -0
- package/skills/platform-custom-metadata-type-generate/scripts/sanitize-developer-name.sh +46 -0
- package/skills/platform-custom-setting-generate/SKILL.md +419 -0
- package/skills/platform-dataspace-access-configure/SKILL.md +3 -3
- package/skills/platform-sharing-owd-configure/SKILL.md +3 -0
- package/skills/platform-value-set-generate/SKILL.md +2 -0
- package/skills/service-agentforce-channel-configure/SKILL.md +29 -20
- package/skills/service-agentforce-channel-configure/assets/BotEmailDefinition.botEmailDefinition-meta.xml +36 -0
- package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email +17 -0
- package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email-meta.xml +29 -0
- package/skills/service-agentforce-channel-configure/assets/mdapi-package.xml +25 -0
- package/skills/service-agentforce-channel-configure/assets/settings-mdapi-package.xml +30 -0
- package/skills/service-agentforce-channel-configure/references/agent-wiring.md +20 -0
- package/skills/service-agentforce-channel-configure/references/botemaildefinition.md +107 -0
- package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +203 -50
- package/skills/service-agentforce-channel-configure/references/channel-types.md +30 -3
- package/skills/service-agentforce-channel-configure/references/queue-resolution.md +17 -8
- package/skills/service-agentforce-channel-configure/references/routing-flow.md +14 -3
- package/skills/service-agentforce-channel-configure/scripts/validate-botemaildefinition.py +126 -0
- package/skills/service-agentforce-channel-configure/scripts/validate-emailtemplate.py +133 -0
- package/skills/service-de-channel-activate/SKILL.md +271 -0
- package/skills/service-de-channel-activate/references/gotchas.md +31 -0
- package/skills/service-de-channel-activate/references/phone-verification.md +96 -0
- package/skills/service-de-channel-activate/references/worked-examples.md +14 -0
- package/skills/service-de-channel-consent-configure/SKILL.md +230 -0
- package/skills/service-de-channel-consent-configure/references/gotchas.md +41 -0
- package/skills/service-de-channel-consent-configure/references/language-keywords.md +84 -0
- package/skills/service-de-channel-consent-configure/references/worked-examples.md +134 -0
- package/skills/service-de-channel-create/SKILL.md +369 -0
- package/skills/service-de-channel-create/references/apple.md +59 -0
- package/skills/service-de-channel-create/references/connect-insert.md +84 -0
- package/skills/service-de-channel-create/references/facebook.md +154 -0
- package/skills/service-de-channel-create/references/line.md +66 -0
- package/skills/service-de-channel-create/references/sms.md +98 -0
- package/skills/service-de-channel-create/references/whatsapp.md +98 -0
- package/skills/service-de-channel-create/references/worked-examples.md +70 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +345 -0
- package/skills/service-de-channel-routing-configure/references/asa-routing.md +90 -0
- package/skills/service-de-channel-routing-configure/references/gotchas.md +27 -0
- package/skills/service-de-channel-routing-configure/references/queue-creation.md +123 -0
- package/skills/service-de-channel-routing-configure/references/target-locate.md +98 -0
- package/skills/service-de-channel-routing-configure/references/worked-examples.md +174 -0
- package/skills/service-de-headless-channel-configure/SKILL.md +305 -0
- package/skills/service-de-headless-channel-configure/references/gotchas.md +27 -0
- package/skills/service-de-headless-channel-configure/references/inputs.md +49 -0
- package/skills/service-de-headless-channel-configure/references/output-envelopes.md +53 -0
- package/skills/service-de-headless-channel-configure/references/partial-success.md +24 -0
- package/skills/service-de-headless-channel-configure/references/terms-and-conditions.md +54 -0
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +108 -0
- package/skills/service-de-waba-integrate/SKILL.md +227 -0
- package/skills/service-digital-engagement-channel-configure/scripts/check-api-version.sh +29 -0
- package/skills/service-digital-engagement-messaging-site-integrate/SKILL.md +10 -9
- package/skills/service-digital-engagement-messaging-site-integrate/references/lwr_patch.md +39 -21
- package/skills/service-digital-engagement-messaging-site-integrate/scripts/patch_lwr_bundle.sh +157 -96
- package/skills/service-email-to-case-configure/SKILL.md +190 -0
- package/skills/service-email-to-case-configure/assets/CaseSettings.settings-meta.xml +45 -0
- package/skills/service-email-to-case-configure/examples/CaseSettings-two-addresses.settings-meta.xml +36 -0
- package/skills/service-email-to-case-configure/references/apply-mechanics.md +73 -0
- package/skills/service-email-to-case-configure/references/routing_address_reference.md +73 -0
- package/skills/service-email-to-case-configure/references/troubleshooting.md +25 -0
- package/skills/service-email-to-case-configure/scripts/apply-casesettings.py +1315 -0
- package/skills/service-email-to-case-configure/scripts/check-agent-email-capability.sh +26 -0
- package/skills/service-email-to-case-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-email-to-case-configure/scripts/tests/_bootstrap.py +45 -0
- package/skills/service-email-to-case-configure/scripts/tests/_fakeorg.py +366 -0
- package/skills/service-email-to-case-configure/scripts/tests/_run.py +84 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_apply_org_scenarios.py +655 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_get_session.py +134 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_new_capabilities.py +193 -0
- package/skills/service-email-to-case-configure/scripts/tests/test_validate_casesettings.py +170 -0
- package/skills/service-email-to-case-configure/scripts/validate-casesettings.py +229 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +33 -15
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -47
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -12
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +5 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +11 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +8 -6
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +3 -3
- package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +11 -3
- package/skills/service-itsm-agentic-setup-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +5 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +11 -7
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +20 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +10 -6
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +7 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +11 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +24 -17
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +98 -58
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +20 -7
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/strip-release-management.mjs +176 -0
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +19 -19
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +3 -4
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +6 -6
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +3 -10
- package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +7 -9
- package/skills/service-itsm-incident-mgmt-configure/SKILL.md +2 -0
- package/skills/service-itsm-incident-priority-configure/SKILL.md +15 -12
- package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +27 -1
- package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +37 -6
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +146 -0
- package/skills/service-native-voice-recording-transcription-configure/assets/confirmation-output.json +16 -0
- package/skills/service-native-voice-recording-transcription-configure/assets/package.xml +22 -0
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +101 -0
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +181 -0
- package/skills/data360-activate/README.md +0 -38
- package/skills/data360-activate/SKILL.md +0 -127
- package/skills/data360-connect/README.md +0 -57
- package/skills/data360-connect/SKILL.md +0 -163
- package/skills/data360-connect/examples/connections/heroku-postgres.json +0 -15
- package/skills/data360-connect/examples/connections/ingest-api-connection.json +0 -5
- package/skills/data360-connect/examples/connections/ingest-api-schema.json +0 -31
- package/skills/data360-connect/examples/connections/redshift.json +0 -16
- package/skills/data360-connect/examples/connections/sharepoint-unstructured.json +0 -20
- package/skills/data360-connect/examples/connections/snowflake-connection.json +0 -42
- package/skills/data360-harmonize/README.md +0 -31
- package/skills/data360-harmonize/SKILL.md +0 -126
- package/skills/data360-orchestrate/README.md +0 -120
- package/skills/data360-orchestrate/SKILL.md +0 -263
- package/skills/data360-orchestrate/assets/definitions/activation-target.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/activation.template.json +0 -7
- package/skills/data360-orchestrate/assets/definitions/calculated-insight.template.json +0 -7
- package/skills/data360-orchestrate/assets/definitions/data-action-target.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/data-action.template.json +0 -5
- package/skills/data360-orchestrate/assets/definitions/data-graph.template.json +0 -21
- package/skills/data360-orchestrate/assets/definitions/data-stream.template.json +0 -55
- package/skills/data360-orchestrate/assets/definitions/dmo.template.json +0 -17
- package/skills/data360-orchestrate/assets/definitions/identity-resolution.template.json +0 -30
- package/skills/data360-orchestrate/assets/definitions/mapping.template.json +0 -14
- package/skills/data360-orchestrate/assets/definitions/relationship.template.json +0 -12
- package/skills/data360-orchestrate/assets/definitions/search-index.template.json +0 -9
- package/skills/data360-orchestrate/assets/definitions/segment.template.json +0 -16
- package/skills/data360-orchestrate/references/feature-readiness.md +0 -157
- package/skills/data360-orchestrate/references/plugin-setup.md +0 -138
- package/skills/data360-orchestrate/scripts/bootstrap-plugin.sh +0 -53
- package/skills/data360-orchestrate/scripts/diagnose-org.mjs +0 -511
- package/skills/data360-orchestrate/scripts/generate-manifest.mjs +0 -68
- package/skills/data360-orchestrate/scripts/verify-plugin.sh +0 -58
- package/skills/data360-prepare/README.md +0 -50
- package/skills/data360-prepare/SKILL.md +0 -203
- package/skills/data360-prepare/examples/ingestion-api/.env.example +0 -8
- package/skills/data360-prepare/examples/ingestion-api/README.md +0 -48
- package/skills/data360-prepare/examples/ingestion-api/send-data.py +0 -144
- package/skills/data360-query/README.md +0 -43
- package/skills/data360-query/SKILL.md +0 -128
- package/skills/data360-query/examples/search-indexes/hybrid-structured.json +0 -44
- package/skills/data360-query/examples/search-indexes/vector-knowledge.json +0 -43
- package/skills/data360-segment/README.md +0 -35
- package/skills/data360-segment/SKILL.md +0 -123
- package/skills/service-helpagent-coordinate/references/channel-help-portal.md +0 -22
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-1-1-non-text-content.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-i-lists.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-ii-tables.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iii-form-labels.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iv-regions.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-v-groups.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-5-identify-input.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-4-3-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-1-1-keyboard.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-4-link-purpose.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-6-headings-labels.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-3-label-in-name.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-7-dragging-movement.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-1-on-focus.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-2-on-input.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-1-error-identification.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-2-labels-instructions.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-3-error-suggestion.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-i-name.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-ii-role.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-iii-value.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-1-1-non-text-content.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-1-use-of-color.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-10-resize-reflow.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-11-non-text-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-3-contrast.md +0 -0
- /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/scripts/contrast-ratio.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-activate-gotchas
|
|
3
|
+
description: "Load when debugging an unexpected result from service-de-channel-activate, or before implementing changes to it. Covers the eleven known gotchas: synchronous PATCH timing for WhatsApp, valid API status transitions, the IsActive sync pass, relationship-name variance across orgs, consent preconditions, DeploymentStatus picklist casing, the REST-vs-Apex equivalence, ErrorReason values, VERIFICATION_REQUIRED handling, keeping auth inside the CLI transport (sf api request rest), and the Status-code-409 Admin API conflict. DO NOT load this during a normal first-time activation run — only when troubleshooting or modifying the skill."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
related-skills: service-de-channel-activate
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Gotchas
|
|
10
|
+
|
|
11
|
+
1. **PATCH is synchronous with Meta `/register` for WhatsApp.** Expect 15-30s response times. Don't set an HTTP client timeout below 60s. The observer runs post-commit but before the HTTP response headers ship — the 204 is the "it all worked" signal. This is why a WhatsApp channel inserted by `service-de-channel-create` takes noticeably longer to activate than an Apple or LINE channel, which have no external callout.
|
|
12
|
+
|
|
13
|
+
2. **Valid API status transitions are gated by `ChannelUsageDeploymentStatusEnum.getValidAPIStatusTransitions()`.** From `Disabled` or `New`, only `Provisioning` is allowed. From `Error`, `Provisioning` (retry) or `Deprovisioning` (give up). From `Active`, only `Deprovisioning`. The DB-only transitions (`Provisioning → Active|Error` and `Deprovisioning → Disabled`) cannot be written by a client — that's the server's job. Don't try to shortcut by PATCHing `Active` directly; the validator returns 400.
|
|
14
|
+
|
|
15
|
+
3. **The second save-hook fire that flips `IsActive` runs in the same observer, not as a separate request.** Inside `MessagingChannelUsageFunctionsHelper.handlePostSave`'s TransactionObserver, after the PLSQL status write, a second pass iterates `messagingChannelObjectsToUpdate` and flips `IsActive` via `setBypassingIsActiveGate(true)`. So one PATCH produces a fully-activated channel — no second client call needed.
|
|
16
|
+
|
|
17
|
+
4. **`MessagingChannelId` is the FK name on MCU, and the parent-to-child relationship name varies across orgs.** On `test1`, `MessagingChannel` has child relationship `MessagingChannelUsages` (plural) so `(SELECT ... FROM MessagingChannelUsages)` works. On `wadtesting`, the child relationship is `None` (unnameable) and the same subquery errors `INVALID_TYPE`. Stage 1 uses two FK-keyed queries to sidestep the variance — don't collapse them back into a subquery.
|
|
18
|
+
|
|
19
|
+
5. **Consent configuration is a precondition validator.** For WhatsApp, `validateChannelReadinessOnProvisioning` requires STOP/HELP keyword consent. If not configured, PATCH fails with `FIELD_INTEGRITY_EXCEPTION` at `validateBeforeSave` time (before the observer even runs). The old Apex method hit the same validator — this isn't a regression. If a caller sees `kind:"readiness-failed"` with a consent message, the fix is either configure consent in Setup → Messaging Settings or programmatically via `MessagingConsentConfig` — not the activation skill's concern.
|
|
20
|
+
|
|
21
|
+
6. **`DeploymentStatus` picklist values are PascalCase.** Specifically `Provisioning`, `Active`, `Error`, `Disabled`, `Deprovisioning`, `New`. Case-sensitive — `PROVISIONING` fails. The values are defined in `ChannelUsageDeploymentStatusEnum` and reflected in the sobject picklist metadata.
|
|
22
|
+
|
|
23
|
+
7. **The REST PATCH is equivalent to the old Apex `activateChannelUsage` method** but without the Aura session machinery or the quirk where that method returned `isSuccessful:false` with a misleading error even on success.
|
|
24
|
+
|
|
25
|
+
8. **If the observer fails partway, the MCU can land on `Error` with `ErrorReason` populated.** Common values for WhatsApp: `ProvisioningError` (Salesforce Admin API rejected — typically "Status code = 409" means phone already registered to another org; see gotcha #11), `MetaRegistrationFailed` (Meta `/register` rejected the phone — could be rate limit, PIN not set, WABA not shared, or credit line not attached), `NotAuthorized` (WABA partnership hasn't propagated). The `ErrorDetails` column carries the raw error response (Salesforce Admin API or Meta Graph API depending on where it failed). Don't swallow these — pass through to the caller's `provisioning-error` envelope.
|
|
26
|
+
|
|
27
|
+
9. **`ErrorReason === "VERIFICATION_REQUIRED"` means phone number needs OTP verification.** When activation fails with this specific ErrorReason on the MessagingChannelUsage, the skill should automatically proceed to Stage 3.2 (WhatsApp verification flow) rather than returning a generic provisioning-error. This is a recoverable error state that we can handle programmatically via the Connect REST verification endpoints. After successful verification, retry activation once (Stage 3.2 Step 5). Live-validated on headless-sdb6 2026-05-21 — verification request → SMS code entry → validation → retry activation succeeded in ~1 minute total.
|
|
28
|
+
|
|
29
|
+
10. **Auth stays inside the CLI transport — use `sf api request rest`, not a manually-extracted bearer token.** The skill used to extract an OAuth token via `SF_TEMP_SHOW_SECRETS=true sf org display` + `jq -r '.result.accessToken'` and pass it as an `Authorization: Bearer` header to `curl`. That pattern put a live access token into shell state (visible in process lists, shell history, and any logging) and was removed as an S1 security anti-pattern. `sf api request rest --target-org '{ORG_ALIAS}'` manages the same OAuth session internally and never surfaces the token to the shell. Historical note: the old pattern's failure mode — omitting `SF_TEMP_SHOW_SECRETS` left the token field null, which surfaced as a 401 `INVALID_AUTH_HEADER` — no longer applies now that the CLI handles auth end-to-end.
|
|
30
|
+
|
|
31
|
+
11. **`ErrorDetails` containing "Status code = 409" comes from the *Salesforce* Admin API, not Meta.** When `ErrorReason = "ProvisioningError"` with `ErrorDetails = "AdminApiRegistrationError__Error occured in admin api with error code and error message Channel Registration response from Admin Api is a failure. Status code = 409"`, this indicates the phone number is already registered to a *different Salesforce org*. The Salesforce Admin API enforces a global constraint: one phone number can only be registered to one org at a time. To resolve: deactivate and deregister the phone from the other org, or use a different phone number for this org. This is distinct from Meta's validation errors (which would appear with `ErrorReason = "MetaRegistrationFailed"` and carry Meta Graph API error codes in `ErrorDetails`).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-activate-phone-verification
|
|
3
|
+
description: "Load when service-de-channel-activate's Stage 3 read comes back with MessagingChannelUsage.ErrorReason === \"VERIFICATION_REQUIRED\" (WhatsApp channels only). Covers the phone-number OTP verification sub-flow: prompting for SMS/voice, requesting and validating the verification code via the Connect livemessage/whatsapp/verification endpoints, and retrying activation once verification succeeds. DO NOT load this for any other ErrorReason, for non-WhatsApp message types, or during the normal happy-path activation run."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
related-skills: service-de-channel-activate
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Stage 3.2: WhatsApp phone number verification (only if ErrorReason === "VERIFICATION_REQUIRED")
|
|
10
|
+
|
|
11
|
+
**This stage only runs for WhatsApp channels when activation fails with `VERIFICATION_REQUIRED`.** The phone number must be verified with Meta before it can be registered. We use the two Connect REST verification endpoints via `sf api request rest`, which manages OAuth internally — no token extraction needed.
|
|
12
|
+
|
|
13
|
+
#### Step 1: Prompt for verification method
|
|
14
|
+
|
|
15
|
+
Ask the user:
|
|
16
|
+
|
|
17
|
+
```text
|
|
18
|
+
WhatsApp phone number verification required.
|
|
19
|
+
|
|
20
|
+
How should we send the verification code?
|
|
21
|
+
|
|
22
|
+
[1] SMS (text message)
|
|
23
|
+
[2] Voice call
|
|
24
|
+
|
|
25
|
+
Choice [1/2]:
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Record `{CODE_METHOD}` as `"SMS"` or `"VOICE"` based on response.
|
|
29
|
+
|
|
30
|
+
#### Step 2: Request verification code
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
sf api request rest \
|
|
34
|
+
"/services/data/v{API_VERSION}/connect/livemessage/whatsapp/verification/request?numberId={PHONE_NUMBER_ID}&codeMethod={CODE_METHOD}" \
|
|
35
|
+
--method POST \
|
|
36
|
+
--target-org '{ORG_ALIAS}' \
|
|
37
|
+
--header 'Accept: application/json' \
|
|
38
|
+
--body '{}' \
|
|
39
|
+
--include \
|
|
40
|
+
> /tmp/amc-verify-request.txt 2>&1
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`{PHONE_NUMBER_ID}` is `{MESSAGING_PLATFORM_KEY}` from Stage 1. `--include` prints the HTTP status/headers block ahead of the body — read the status from that block rather than a trailing marker line.
|
|
44
|
+
|
|
45
|
+
**Important:** The endpoint expects query parameters, not a JSON body. Pass `numberId` and `codeMethod` as URL parameters. Still send `--body '{}'` (not `--body ''`): `sf api request rest` treats an empty body string as no body and fails locally with `No 'mode' found in 'body' entry` before the request is issued, so an empty `{}` object is the way to make a body-less POST reach the endpoint.
|
|
46
|
+
|
|
47
|
+
| HTTP status | Body | Handling |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| 200 or 204 | Empty or `{"success": true, "displayNumber": "..."}` | Success. Continue to Step 3. Optionally extract `displayNumber` from response to show user which phone will receive the code. |
|
|
50
|
+
| 400 | `[{errorCode: "...", message: "..."}]` | Business error. Common: code already requested recently (Meta rate limit). Emit `{ok:false, kind:"verification-request-failed", errorCode, message, hint:"Meta may be rate-limiting requests. Wait a few minutes and retry."}`. |
|
|
51
|
+
| 401 | (empty) | Bearer token invalid. Emit `{ok:false, kind:"auth"}`. |
|
|
52
|
+
| 404 | HTML | Endpoint unavailable. Emit `{ok:false, kind:"transport", status:404, hint:"Verification endpoint not available — check API version"}`. |
|
|
53
|
+
|
|
54
|
+
#### Step 3: Prompt for verification code
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
A {CODE_METHOD} verification code has been sent to the phone number.
|
|
58
|
+
Please check the device and enter the 6-digit code:
|
|
59
|
+
|
|
60
|
+
Code:
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Read user input, trim whitespace, **remove any hyphens**, record as `{VERIFICATION_CODE}`. Must be exactly 6 digits after hyphen removal. Users often enter codes like `123-456`; we must strip the hyphen and send `123456` in the request body.
|
|
64
|
+
|
|
65
|
+
#### Step 4: Validate verification code
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
sf api request rest \
|
|
69
|
+
"/services/data/v{API_VERSION}/connect/livemessage/whatsapp/verification/validate?numberId={PHONE_NUMBER_ID}&verificationCode={VERIFICATION_CODE}" \
|
|
70
|
+
--method POST \
|
|
71
|
+
--target-org '{ORG_ALIAS}' \
|
|
72
|
+
--header 'Accept: application/json' \
|
|
73
|
+
--body '{}' \
|
|
74
|
+
--include \
|
|
75
|
+
> /tmp/amc-verify-validate.txt 2>&1
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Important:** The endpoint expects query parameters, not a JSON body. Pass `numberId` and `verificationCode` as URL parameters. Send `--body '{}'` rather than `--body ''` — an empty body string makes `sf api request rest` fail locally with `No 'mode' found in 'body' entry` before the POST is sent.
|
|
79
|
+
|
|
80
|
+
| HTTP status | Body | Handling |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| 200 or 204 | Empty or `{"success": true}` | Success. Number is now verified. Continue to Step 5 (retry activation). |
|
|
83
|
+
| 400 | `[{errorCode: "...", message: "..."}]` | Invalid code or rate limit. Parse error message: if "wrong code" or "guessing too fast", prompt: "Invalid verification code. Try again? [y/n]". If `y`, repeat Step 3 (up to 3 total attempts). If `n` or after 3 failures, emit `{ok:false, kind:"verification-failed", hint:"Verification code validation failed"}`. |
|
|
84
|
+
| 401 | (empty) | Bearer token invalid. Emit `{ok:false, kind:"auth"}`. |
|
|
85
|
+
|
|
86
|
+
Common Meta error codes from the UI component:
|
|
87
|
+
- `136024`: Too many recent attempts (rate limit). Emit `{ok:false, kind:"verification-failed", hint:"Too many verification attempts. Wait a few minutes and retry activation."}`.
|
|
88
|
+
- `136025`: Wrong code or guessing too fast. Allow retry (up to 3 attempts total).
|
|
89
|
+
|
|
90
|
+
#### Step 5: Retry activation after verification
|
|
91
|
+
|
|
92
|
+
The phone number is now verified. Return to Stage 2 (PATCH activation) with a retry counter to avoid infinite loops. Maximum 1 retry after verification.
|
|
93
|
+
|
|
94
|
+
If the second activation attempt also fails with `VERIFICATION_REQUIRED`, emit `{ok:false, kind:"verification-failed", hint:"Phone number verification succeeded but activation still reports VERIFICATION_REQUIRED — unusual, check Meta Business Manager"}`.
|
|
95
|
+
|
|
96
|
+
If the second activation succeeds (`Active`), proceed to Stage 4 as normal.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-activate-worked-examples
|
|
3
|
+
description: "Load when you want a reference run of service-de-channel-activate's activation flow — a full end-to-end trace for a given path or failure mode. Covers WhatsApp activation via REST PATCH (the reference happy-path), Apple Business Chat activation, a readiness-validator failure (missing consent), and an already-active no-op. DO NOT load this during normal execution of the skill's stages — only when a concrete trace is wanted for reference or debugging."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
related-skills: service-de-channel-activate
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Worked examples: `service-de-channel-activate`
|
|
10
|
+
|
|
11
|
+
- WhatsApp activation on wadtesting via REST PATCH (verified 2026-04-30 — the reference happy-path, ~21s wall-clock)
|
|
12
|
+
- Apple Business Chat activation (sub-second, no external call)
|
|
13
|
+
- Readiness-validator failure (missing consent)
|
|
14
|
+
- Already-active no-op
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-consent-configure
|
|
3
|
+
description: "Configure consent (opt-in/opt-out) on an Enhanced `MessagingChannel` so activation will accept it. Given a `{CHANNEL_ID}`, sets the channel's `ConsentType` (ImplicitOptIn / ExplicitOptIn / DoubleOptIn) and the `OptInPrompt` / `DoubleOptInPrompt` prompt fields, then creates or updates one `MsgChannelLanguageKeyword` record per language holding the opt-in / double-opt-in / opt-out / help / custom keywords and confirmation messages. Use between channel insertion and activation — activation fails the consent-readiness check unless a valid ConsentType and at least one matching language-keyword record exist."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
minApiVersion: "67.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
cliTools:
|
|
9
|
+
- tool: ["sf"]
|
|
10
|
+
semver: ">=2.0.0"
|
|
11
|
+
relatedSkills:
|
|
12
|
+
- "service-de-channel-activate"
|
|
13
|
+
- "service-de-channel-routing-configure"
|
|
14
|
+
- "service-de-headless-channel-configure"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Configuring Channel Consent
|
|
18
|
+
|
|
19
|
+
## What this skill does
|
|
20
|
+
|
|
21
|
+
Ensures an Enhanced `MessagingChannel` has valid **consent** configured before activation. Consent has two parts, and both are checked by the server-side activation-readiness gate (`MessagingChannelReadinessChecker.isConsentConfiguredForChannel`):
|
|
22
|
+
|
|
23
|
+
1. **Channel-level fields** on `MessagingChannel`:
|
|
24
|
+
- `ConsentType` — static enum: `ImplicitOptIn`, `ExplicitOptIn`, or `DoubleOptIn`.
|
|
25
|
+
- `OptInPrompt` — message sent when an end-user reply doesn't match an opt-in keyword (required for ExplicitOptIn/DoubleOptIn, except on Unified SMS / WhatsApp / RCS where it's optional).
|
|
26
|
+
- `DoubleOptInPrompt` — message sent when a reply doesn't match a double-opt-in keyword (required for DoubleOptIn).
|
|
27
|
+
- `IsRequireDoubleOptIn` — boolean flag; set true alongside `ConsentType=DoubleOptIn`.
|
|
28
|
+
|
|
29
|
+
2. **Per-language keyword records** — one or more `MsgChannelLanguageKeyword` child rows (master-detail to `MessagingChannel`, key prefix `3Or`). Each holds the keywords an end-user sends (opt-in, double-opt-in, opt-out, help, custom) and the confirmation replies, **for one language**. Keyword fields are **comma-separated strings** (e.g. `"STOP,UNSUBSCRIBE,CANCEL"`).
|
|
30
|
+
|
|
31
|
+
**Consent is never a no-op for activation.** Even `ImplicitOptIn` requires at least one `MsgChannelLanguageKeyword` with an opt-out keyword + opt-out confirmation. (The one exception is an outbound-only channel, which the readiness check passes automatically.)
|
|
32
|
+
|
|
33
|
+
Consent settings apply only to **Enhanced** channels (`PlatformType=Enhanced`) of a supported message type. On a Standard channel these fields aren't used; this skill only touches Enhanced channels.
|
|
34
|
+
|
|
35
|
+
## Where this fits
|
|
36
|
+
|
|
37
|
+
This skill runs between routing and activation in the Enhanced-channel setup pipeline. `service-de-channel-routing-configure` sets the channel's `SessionHandler`; this skill configures consent; then `service-de-channel-activate` flips the channel live. The activation readiness check requires **both** routing and consent, so run this before activating. The `service-de-headless-channel-configure` orchestrator sequences all three automatically — invoke this skill directly only when you want to configure consent on its own.
|
|
38
|
+
|
|
39
|
+
## Reference File Index
|
|
40
|
+
|
|
41
|
+
| Reference file | Load when |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| `references/language-keywords.md` | Creating or updating the per-language `MsgChannelLanguageKeyword` records — full field list, CSV keyword semantics, create-vs-update logic, and the Unified-SMS help/custom-keyword rules. |
|
|
44
|
+
| `references/gotchas.md` | Troubleshooting an unexpected result, or before modifying this skill. |
|
|
45
|
+
| `references/worked-examples.md` | You want a reference run of the ImplicitOptIn, ExplicitOptIn, or DoubleOptIn paths. |
|
|
46
|
+
|
|
47
|
+
## When NOT to use this skill
|
|
48
|
+
|
|
49
|
+
- **The channel isn't Enhanced.** Consent fields apply to Enhanced channels only. This skill confirms `PlatformType=Enhanced` and stops otherwise.
|
|
50
|
+
- **The channel is outbound-only.** Outbound-only channels pass the consent-readiness check automatically; there's nothing to configure.
|
|
51
|
+
- **The channel doesn't exist yet.** Run the insertion skill first; this skill expects a real `MessagingChannel.Id`.
|
|
52
|
+
|
|
53
|
+
## Inputs (from caller)
|
|
54
|
+
|
|
55
|
+
- `{CHANNEL_ID}` — a 15- or 18-char `MessagingChannel.Id` (prefix `0Mj`). Must already exist and be Enhanced.
|
|
56
|
+
- `{CONSENT_TYPE}` — optional; one of `ImplicitOptIn` | `ExplicitOptIn` | `DoubleOptIn`. If omitted, the skill prompts.
|
|
57
|
+
- `{LANGUAGE}` — optional; the `MasterLanguage` for the keyword record (e.g. `en_US`). Defaults to `en_US` if not provided.
|
|
58
|
+
- `{ORG_ALIAS}` — optional; `sf` CLI target-org alias. Default: whatever `sf config get target-org` returns.
|
|
59
|
+
|
|
60
|
+
The individual keyword/confirmation/prompt values are collected interactively in Stage 3–4 (or accepted from the caller if pre-supplied).
|
|
61
|
+
|
|
62
|
+
## Output (to caller)
|
|
63
|
+
|
|
64
|
+
**Success — no change needed:**
|
|
65
|
+
```json
|
|
66
|
+
{"ok": true, "noop": true, "consentType": "ImplicitOptIn|ExplicitOptIn|DoubleOptIn", "languageKeywordIds": ["3Or..."], "message": "Consent already configured and activation-ready"}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Success — consent configured:**
|
|
70
|
+
```json
|
|
71
|
+
{"ok": true, "consentType": "ExplicitOptIn", "channelFieldsSet": ["ConsentType","OptInPrompt"],
|
|
72
|
+
"languageKeywordId": "3Or...", "language": "en_US", "created": true|false, "activationReady": true}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Precondition / validation not met:**
|
|
76
|
+
```json
|
|
77
|
+
{"ok": false, "kind": "not-enhanced", "hint": "consent fields apply to Enhanced channels only; this channel is PlatformType=<x>"}
|
|
78
|
+
{"ok": false, "kind": "missing-consent-input", "hint": "<which required keyword/prompt for the chosen ConsentType is missing>"}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Failure:**
|
|
82
|
+
```json
|
|
83
|
+
{"ok": false, "kind": "channel-patch-failed", "message": "..."}
|
|
84
|
+
{"ok": false, "kind": "keyword-record-failed", "message": "..."}
|
|
85
|
+
{"ok": false, "kind": "verify-failed", "hint": "writes returned success but a re-read shows the channel is still not consent-ready"}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Stage 1: Read current consent state
|
|
91
|
+
|
|
92
|
+
Read the channel's consent fields and any existing language-keyword records.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
96
|
+
--query "SELECT Id, DeveloperName, PlatformType, MessageType, MessagingPlatformKey, ConsentType, OptInPrompt, DoubleOptInPrompt, IsRequireDoubleOptIn FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" \
|
|
97
|
+
--json > /tmp/cc-channel.json
|
|
98
|
+
|
|
99
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
100
|
+
--query "SELECT Id, MasterLanguage, OptInKeywords, DoubleOptInKeywords, OptInConfirmation, OptOutKeywords, OptOutConfirmation, HelpKeywords, HelpResponse, CustomKeywords, CustomResponse FROM MsgChannelLanguageKeyword WHERE MessagingChannelId = '{CHANNEL_ID}'" \
|
|
101
|
+
--json > /tmp/cc-keywords.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If the channel record is missing — halt with `Error: Channel {CHANNEL_ID} not found — check the id, or run the insertion skill first.`
|
|
105
|
+
|
|
106
|
+
If `PlatformType != 'Enhanced'` — emit `{ok:false, kind:"not-enhanced", ...}` and stop.
|
|
107
|
+
|
|
108
|
+
**No-op check:** if `ConsentType` is non-null AND the existing language-keyword records already satisfy the readiness matrix for that ConsentType (see Stage 5), emit the `noop:true` envelope and return. Don't re-prompt or overwrite a channel that is already activation-ready.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Stage 2: Choose the ConsentType
|
|
113
|
+
|
|
114
|
+
If `{CONSENT_TYPE}` wasn't supplied, prompt (do NOT auto-pick):
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
Channel '{developerName}' ({CHANNEL_ID}) — choose a consent model:
|
|
118
|
+
|
|
119
|
+
1) ImplicitOptIn — end users are opted in by default; you only handle opt-OUT (STOP) keywords.
|
|
120
|
+
2) ExplicitOptIn — end users must send an opt-in keyword before you can message them.
|
|
121
|
+
3) DoubleOptIn — explicit opt-in plus a second confirming keyword.
|
|
122
|
+
|
|
123
|
+
Pick [1-3]:
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Record `{CONSENT_TYPE}`. Note the field checks that follow (this is the exact server-side activation-readiness contract — see `references/gotchas.md` for the source):
|
|
127
|
+
|
|
128
|
+
| ConsentType | MessagingChannel fields | `MsgChannelLanguageKeyword` (≥1 record) |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| ImplicitOptIn | (none required) | `OptOutKeywords` + `OptOutConfirmation` |
|
|
131
|
+
| ExplicitOptIn | `OptInPrompt`¹ | above + `OptInKeywords` |
|
|
132
|
+
| DoubleOptIn | `OptInPrompt`¹ + `DoubleOptInPrompt` + `IsRequireDoubleOptIn=true` | above + `DoubleOptInKeywords` |
|
|
133
|
+
|
|
134
|
+
¹ `OptInPrompt` is optional (not required for readiness) on Unified SMS / WhatsApp / RCS channels — detect via `MessageType IN ('Text','WhatsApp','Rcs')` on an Enhanced channel (the queryable proxy for the server's `isUnifiedSMSOrWhatsappOrRCS`). Still recommend setting it for a good end-user experience.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Stage 3: Collect channel-level prompt values
|
|
139
|
+
|
|
140
|
+
Only prompt for the fields the chosen ConsentType requires (see the matrix above). Skip this stage entirely for `ImplicitOptIn` — it needs no channel-level prompt fields.
|
|
141
|
+
|
|
142
|
+
For `ExplicitOptIn` / `DoubleOptIn`:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
Opt-in prompt (sent when a reply doesn't match an opt-in keyword):
|
|
146
|
+
```
|
|
147
|
+
For `DoubleOptIn`, also:
|
|
148
|
+
```text
|
|
149
|
+
Double-opt-in prompt (sent when a reply doesn't match a double-opt-in keyword):
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Trim whitespace. If a required prompt is empty after trimming, re-prompt once; on a second empty, emit `{ok:false, kind:"missing-consent-input", hint:"..."}`.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Stage 4: Configure the per-language keyword record
|
|
157
|
+
|
|
158
|
+
Collect the keyword lists and confirmation replies for `{LANGUAGE}` (default `en_US`), then create or update the `MsgChannelLanguageKeyword` record.
|
|
159
|
+
|
|
160
|
+
**Load `references/language-keywords.md` and follow it** — it holds the full field list, the comma-separated-keyword semantics, the create-vs-update decision (reuse an existing record for the same language rather than duplicating), and the Unified-SMS help/custom-keyword rules.
|
|
161
|
+
|
|
162
|
+
The minimum per ConsentType (again, from the readiness contract):
|
|
163
|
+
- **ImplicitOptIn:** `OptOutKeywords` (e.g. `STOP,UNSUBSCRIBE`) + `OptOutConfirmation`.
|
|
164
|
+
- **ExplicitOptIn:** the above + `OptInKeywords` (e.g. `START,YES`).
|
|
165
|
+
- **DoubleOptIn:** the above + `DoubleOptInKeywords` (e.g. `CONFIRM`).
|
|
166
|
+
|
|
167
|
+
`OptInConfirmation`, `HelpKeywords`/`HelpResponse`, and `CustomKeywords`/`CustomResponse` are optional for the base readiness check (help/custom are only *required* on Unified SMS — see the reference), but collect them if the user wants them.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Stage 5: Write channel fields + keyword record
|
|
172
|
+
|
|
173
|
+
Write the channel-level fields in one PATCH (only the fields the ConsentType requires).
|
|
174
|
+
|
|
175
|
+
**Quoting rule (load-bearing):** `sf data update record --values` splits on spaces, so any value containing a space **must be wrapped in single quotes inside the double-quoted `--values` string** — e.g. `OptInPrompt='Reply YES to receive messages'`. An unquoted `OptInPrompt=Reply YES ...` fails with `Malformed key=value pair` (verified live on sdb6c). Prompts almost always contain spaces, so always quote them.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# ImplicitOptIn — ConsentType only
|
|
179
|
+
sf data update record --target-org '{ORG_ALIAS}' --sobject MessagingChannel \
|
|
180
|
+
--record-id '{CHANNEL_ID}' --values "ConsentType=ImplicitOptIn" --json > /tmp/cc-patch.json
|
|
181
|
+
|
|
182
|
+
# ExplicitOptIn
|
|
183
|
+
sf data update record --target-org '{ORG_ALIAS}' --sobject MessagingChannel \
|
|
184
|
+
--record-id '{CHANNEL_ID}' --values "ConsentType=ExplicitOptIn OptInPrompt='{OPT_IN_PROMPT}'" --json > /tmp/cc-patch.json
|
|
185
|
+
|
|
186
|
+
# DoubleOptIn
|
|
187
|
+
sf data update record --target-org '{ORG_ALIAS}' --sobject MessagingChannel \
|
|
188
|
+
--record-id '{CHANNEL_ID}' \
|
|
189
|
+
--values "ConsentType=DoubleOptIn OptInPrompt='{OPT_IN_PROMPT}' DoubleOptInPrompt='{DBL_PROMPT}' IsRequireDoubleOptIn=true" --json > /tmp/cc-patch.json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
If `status !== 0`: emit `{ok:false, kind:"channel-patch-failed", message: ...}` and return.
|
|
193
|
+
|
|
194
|
+
Then create or update the `MsgChannelLanguageKeyword` record per `references/language-keywords.md`. If it fails: `{ok:false, kind:"keyword-record-failed", message: ...}`.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Stage 6: Verify activation readiness
|
|
199
|
+
|
|
200
|
+
Re-read both the channel and its keyword records (same two queries as Stage 1). Confirm, per the chosen ConsentType's row in the Stage 2 matrix, that every required field is now non-null on the channel AND on at least one keyword record for the configured language.
|
|
201
|
+
|
|
202
|
+
If any required field is still null: emit `{ok:false, kind:"verify-failed", hint:"<which requirement is unmet>"}`.
|
|
203
|
+
|
|
204
|
+
Otherwise emit the success envelope with `activationReady: true`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Stage 7: Report to caller
|
|
209
|
+
|
|
210
|
+
Report the JSON envelope. If this skill is the leaf (user invoked it directly), render:
|
|
211
|
+
|
|
212
|
+
- `Success — Consent configured — channel {CHANNEL_ID} is now {CONSENT_TYPE} with a {LANGUAGE} keyword record. Activation-ready.`
|
|
213
|
+
- `Info: Consent already configured — {CONSENT_TYPE}, {N} language record(s). No changes.` (no-op path)
|
|
214
|
+
- `Warning: This channel is PlatformType={x}, not Enhanced — consent fields don't apply here.` (not-enhanced)
|
|
215
|
+
- `Warning: Missing required consent input for {CONSENT_TYPE}: {what}. Re-run and provide it.` (missing-consent-input)
|
|
216
|
+
- `Error: {kind}: {message}` (other failures)
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Worked examples
|
|
221
|
+
|
|
222
|
+
For reference runs of the ImplicitOptIn (opt-out only), ExplicitOptIn, and DoubleOptIn paths, see `references/worked-examples.md`.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Gotchas
|
|
227
|
+
|
|
228
|
+
Known gotchas — the exact per-ConsentType activation-readiness contract (and its source), comma-separated keyword storage, the "ImplicitOptIn still needs a keyword record" trap, one-record-per-language uniqueness, the Unified-SMS help/French-custom rules, and the OptInPrompt Unified-SMS/WhatsApp/RCS exemption.
|
|
229
|
+
|
|
230
|
+
**When troubleshooting an unexpected result, or before modifying this skill, load `references/gotchas.md` and follow it.**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-consent-configure-gotchas
|
|
3
|
+
description: "Load when debugging an unexpected result from service-de-channel-consent-configure, or before implementing changes to it. Covers the known gotchas: the exact per-ConsentType activation-readiness contract and its source, the ImplicitOptIn-still-needs-a-keyword-record trap, comma-separated keyword storage, one-record-per-language uniqueness, the Unified-SMS help/French-custom rules, the OptInPrompt Unified-SMS/WhatsApp/RCS exemption, the outbound-only auto-pass, the Enhanced-only gate, the legacy OptInResponse/OptOutResponse deprecation, and why MsgChannelLanguageKeyword must be written over the Data/SObjects API rather than the Tooling API despite its setupEntity perApiAccess block. DO NOT load this during a normal first-time consent run — only when troubleshooting or modifying the skill."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
related-skills: service-de-channel-consent-configure
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Gotchas
|
|
10
|
+
|
|
11
|
+
1. **The activation-readiness contract lives in `MessagingChannelReadinessChecker.isConsentConfiguredForChannel`.** This is the authority — the UI (`channelRecordHome.js`, `languageOptionsModal.js`) mirrors it but is advisory. The three validators are:
|
|
12
|
+
- `checkIfChannelIsConfiguredForImplicitOptIn`: at least one `MsgChannelLanguageKeyword` with `OptOutKeywords != null && OptOutConfirmation != null`.
|
|
13
|
+
- `checkIfChannelIsConfiguredForExplicitOptIn`: implicit rule **plus** `OptInKeywords != null`, **plus** on the channel `(OptInPrompt != null || isUnifiedSMSOrWhatsappOrRCS)`.
|
|
14
|
+
- `checkIfChannelIsConfiguredForDoubleOptIn`: explicit rule **plus** `DoubleOptInKeywords != null`, **plus** on the channel `DoubleOptInPrompt != null`.
|
|
15
|
+
Consent is ANDed with routing in `isChannelReadyForActivation` — a channel with perfect routing still fails activation if consent is unmet.
|
|
16
|
+
|
|
17
|
+
2. **ImplicitOptIn is NOT a no-op.** The common misconception is that "implicit" means "nothing to configure." It still requires a `MsgChannelLanguageKeyword` record with opt-out keyword + opt-out confirmation. A channel set to `ConsentType=ImplicitOptIn` with zero keyword records fails the readiness check. This is the single most likely reason a freshly-configured channel won't activate.
|
|
18
|
+
|
|
19
|
+
3. **Outbound-only channels pass consent automatically.** `isOutboundOnlyChannel` short-circuits `isConsentConfiguredForChannel` to true. Don't try to configure consent on an outbound-only channel — there's nothing to satisfy, and the skill's "When NOT to use" section bails on this case.
|
|
20
|
+
|
|
21
|
+
4. **Keywords are comma-separated strings, not picklists.** Store `OptOutKeywords` as `STOP,UNSUBSCRIBE,CANCEL`. The server splits on comma and trims. A JSON array or newline-separated value will not match correctly. See `references/language-keywords.md`.
|
|
22
|
+
|
|
23
|
+
5. **One `MsgChannelLanguageKeyword` record per (channel, language).** Don't create a second record for a language that already has one — PATCH the existing record. Duplicate language records are ambiguous for the readiness check (it only needs *one* satisfying record, but duplicates make the UI and future edits inconsistent).
|
|
24
|
+
|
|
25
|
+
6. **`OptInPrompt` is exempt from the readiness check on Unified SMS / WhatsApp / RCS.** `isUnifiedSMSOrWhatsappOrRCS` makes `OptInPrompt` optional for the ExplicitOptIn/DoubleOptIn readiness rule. For all other channel types, `OptInPrompt` is required for ExplicitOptIn and DoubleOptIn. Note: `DoubleOptInPrompt` has NO such exemption — it is always required for DoubleOptIn regardless of channel type. **Detecting the exemption from a query:** there is no `IsUnifiedMessagingEnabled` column on the Data-API `MessagingChannel` sObject (verified via `sf sobject describe` on sdb6c 2026-08-13 — the queryable surface is `MessageType`, `PlatformType`, `MessagingPlatformKey`). Use `PlatformType='Enhanced' AND MessageType IN ('Text','WhatsApp','Rcs')` as the proxy for `isUnifiedSMSOrWhatsappOrRCS`. `MessageType` picklist values on this org include `Text`, `WhatsApp`, `Rcs`, `Facebook`, `AppleBusinessChat`, `EmbeddedMessaging`, `Line`, etc.
|
|
26
|
+
|
|
27
|
+
7. **Unified SMS adds two mandatory keyword-record rules.** Only when the channel is Unified SMS: `HelpKeywords`+`HelpResponse` become mandatory, and a `fr_CA` record additionally needs `CustomKeywords`+`CustomResponse`. These are enforced by the readiness check for Unified SMS and are irrelevant (optional) on WhatsApp/Facebook/etc.
|
|
28
|
+
|
|
29
|
+
8. **Consent applies to Enhanced channels only.** The UI gates the consent panel with `consentSettingsEnabled = platformType === 'Enhanced' && supportedMessageTypes.includes(messageType)`. On a Standard channel these fields are unused. Stage 1 confirms `PlatformType=Enhanced` and emits `kind:"not-enhanced"` otherwise.
|
|
30
|
+
|
|
31
|
+
9. **Legacy `OptInResponse` / `OptOutResponse` on `MessagingChannel` are deprecated (API 224).** Do NOT write them — the modern surface is `ConsentType` + `OptInPrompt` + `DoubleOptInPrompt` on the channel, and the per-language responses on `MsgChannelLanguageKeyword` (`OptInConfirmation`/`OptOutConfirmation`). The readiness check does not read the legacy fields.
|
|
32
|
+
|
|
33
|
+
10. **Switching ConsentType down should null the now-unused prompts.** The UI nulls `OptInPrompt` and `DoubleOptInPrompt` when moving to ImplicitOptIn, and nulls `DoubleOptInPrompt` when moving to ExplicitOptIn. If you re-run this skill to *lower* a channel's consent level, follow the same discipline so stale prompt text doesn't linger — though leaving them set does not fail the readiness check, it misrepresents the channel's configuration.
|
|
34
|
+
|
|
35
|
+
11. **WhatsApp channels with rich content reject `ExplicitOptIn`.** Setting `ConsentType=ExplicitOptIn` on a WhatsApp channel that has rich content is rejected server-side with `INVALID_INPUT: Consent Type for WhatsApp Messaging Channel with rich content cannot be set to EXPLICIT_OPT_IN` (verified live on sdb6c 2026-08-13). WhatsApp rich-content channels are effectively `ImplicitOptIn`. If the user asks for ExplicitOptIn/DoubleOptIn on such a channel, surface this constraint rather than retrying — emit `kind:"channel-patch-failed"` with the server message. Not all message types accept all ConsentTypes; the server is the authority.
|
|
36
|
+
|
|
37
|
+
12. **The `sf data ... --values` quoting rule is load-bearing.** The CLI tokenizes `--values` on spaces, so every multi-word value (all prompts, all confirmation messages) must be single-quoted *inside* the double-quoted `--values` string: `OptInPrompt='Reply YES to receive messages'`. Unquoted, it fails before hitting the server with `Malformed key=value pair for value: <second word>` (verified live). Keyword CSVs without spaces (`OptOutKeywords=stop,unsubscribe`) don't need quoting. To null a prompt when down-switching ConsentType, pass `OptInPrompt=''` — verified to set the field to null.
|
|
38
|
+
|
|
39
|
+
13. **`ConsentType` requires minApiVersion 224.** All consent fields (`ConsentType`, `OptInPrompt`, `DoubleOptInPrompt`, `IsRequireDoubleOptIn`) and the `MsgChannelLanguageKeyword` entity were introduced in API 224. The skill declares `minApiVersion: "67.0"` (well above 224); on an older org the fields won't exist and the Stage 1 query will error.
|
|
40
|
+
|
|
41
|
+
14. **Write `MsgChannelLanguageKeyword` over the regular Data/SObjects API — NOT the Tooling API.** `sf data create/update record --sobject MsgChannelLanguageKeyword` (the path this skill uses) works and is the sanctioned write path — verified live on sdb6c 2026-08-13: a `sf data update record` returned `success=true` at API v69.0, and `sf sobject describe` reports the object `createable: true, updateable: true`. **The entity XML is misleading here.** `MsgChannelLanguageKeyword.entity.xml` is a `setupEntity` carrying `<perApiAccess minApiVersion="246" apiAccess="isDevInternal || isUiTier" sobjectFamily="TOOLING"/>`. That block reads like a blanket write gate, but `sobjectFamily="TOOLING"` scopes it to the **Tooling API family only** — `ApiDictionaryInitializer._getFieldAccessStrategy` selects the `TOOLING` `perApiAccess` seed exclusively when `apiFamily.isTooling()`, so a regular SObjects/Data API request never applies it and instead uses the entity's base `createAccess`/`editAccess` (`LiveMessage.userCanCustomizeSetup`, which a setup-capable admin satisfies). Consequently a **Tooling** API write (`sf ... --use-tooling-api`, or the Tooling REST/SOAP endpoints) returns `NOT_FOUND` for this entity for external callers, while the Data API write succeeds. If a keyword write fails, first suspect a transient org issue (sdb6c 503 maintenance windows produced spurious `UNKNOWN_EXCEPTION` on Data-API writes during testing) and retry — do NOT switch to the Tooling API thinking it's the "real" write path; it isn't.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Per-language keyword records: `MsgChannelLanguageKeyword`
|
|
2
|
+
|
|
3
|
+
Load this when Stage 4 needs to create or update the per-language keyword record for a channel.
|
|
4
|
+
|
|
5
|
+
## Entity shape
|
|
6
|
+
|
|
7
|
+
`MsgChannelLanguageKeyword` (key prefix `3Or`, introduced in API 224) is a **master-detail child of `MessagingChannel`**. One record holds all the consent keywords and confirmation messages for **one language** on one channel. A channel can have many of these — one per supported language.
|
|
8
|
+
|
|
9
|
+
Fields (all string/CLOB except the FK):
|
|
10
|
+
|
|
11
|
+
| Field | Meaning | Keyword or response |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| `MasterLanguage` | **Required.** Language locale for this record, e.g. `en_US`, `fr_CA`, `es`, `pt_BR`. | — |
|
|
14
|
+
| `MessagingChannelId` | **Required.** Master-detail FK to the channel (`0Mj…`). Set on create only. | — |
|
|
15
|
+
| `OptInKeywords` | Keywords an end user sends to opt in (e.g. `START,YES,SUBSCRIBE`). | keyword list |
|
|
16
|
+
| `OptInConfirmation` | Reply sent after a successful opt-in. | response |
|
|
17
|
+
| `DoubleOptInKeywords` | Keywords an end user sends to confirm a double opt-in (e.g. `CONFIRM`). | keyword list |
|
|
18
|
+
| `OptOutKeywords` | Keywords an end user sends to opt out (e.g. `STOP,UNSUBSCRIBE,CANCEL`). | keyword list |
|
|
19
|
+
| `OptOutConfirmation` | Reply sent after a successful opt-out. | response |
|
|
20
|
+
| `HelpKeywords` | Keywords an end user sends to get help (e.g. `HELP,INFO`). | keyword list |
|
|
21
|
+
| `HelpResponse` | Reply sent for a help request. | response |
|
|
22
|
+
| `CustomKeywords` | Extra keywords the channel handles. | keyword list |
|
|
23
|
+
| `CustomResponse` | Reply sent for a custom keyword match. | response |
|
|
24
|
+
| `MessagingChannelUsage` | Optional FK (minApiVersion 252); leave unset unless the caller has a specific usage row. | — |
|
|
25
|
+
|
|
26
|
+
## Keyword storage format — comma-separated strings
|
|
27
|
+
|
|
28
|
+
Keyword fields hold a **comma-separated string**, not a multi-value/picklist. The UI joins the entered words with `wordArray.toString()` and the server splits them with `TextUtil.splitSimpleAndTrim(value, ",", 0)`. So:
|
|
29
|
+
|
|
30
|
+
- Store `OptOutKeywords` as the literal string `STOP,UNSUBSCRIBE,CANCEL`.
|
|
31
|
+
- Whitespace around each token is trimmed server-side, but keep it clean: no leading/trailing commas, no empty tokens.
|
|
32
|
+
- Matching is on the whole inbound message against the token list; keep keywords single-word and uppercase by convention (the UI upper-cases nothing automatically — follow the org's existing records if any).
|
|
33
|
+
|
|
34
|
+
## Create vs update — reuse the record for a language
|
|
35
|
+
|
|
36
|
+
**There is one record per (channel, language).** Before creating, check the Stage 1 read (`/tmp/cc-keywords.json`) for an existing `MsgChannelLanguageKeyword` whose `MasterLanguage` equals `{LANGUAGE}`:
|
|
37
|
+
|
|
38
|
+
- **Exists →** `updateRecord` path: PATCH that record's Id with the new field values. Do NOT create a second record for the same language — duplicates confuse the readiness check and the UI.
|
|
39
|
+
- **Doesn't exist →** `createRecord` path: insert a new record, setting `MessagingChannelId={CHANNEL_ID}` and `MasterLanguage={LANGUAGE}` plus the collected keyword/response fields.
|
|
40
|
+
|
|
41
|
+
### Create
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
sf data create record --target-org '{ORG_ALIAS}' --sobject MsgChannelLanguageKeyword \
|
|
45
|
+
--values "MessagingChannelId={CHANNEL_ID} MasterLanguage={LANGUAGE} \
|
|
46
|
+
OptOutKeywords='STOP,UNSUBSCRIBE' OptOutConfirmation='You have been unsubscribed.'" \
|
|
47
|
+
--json > /tmp/cc-kw-write.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Add `OptInKeywords`/`OptInConfirmation` for ExplicitOptIn, and `DoubleOptInKeywords` for DoubleOptIn.
|
|
51
|
+
|
|
52
|
+
**Quoting (load-bearing):** `sf data ... --values` splits on spaces, so wrap any value containing a space in single quotes *inside* the double-quoted `--values` string — including keyword CSVs if you ever add spaces after commas, and every confirmation/response message. A comma-separated keyword list with no spaces (`OptOutKeywords=stop,unsubscribe,cancel`) does not need quoting, but a message (`OptOutConfirmation='You have been unsubscribed.'`) always does. An unquoted multi-word value fails with `Malformed key=value pair` (verified live on sdb6c 2026-08-13).
|
|
53
|
+
|
|
54
|
+
### Update
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
sf data update record --target-org '{ORG_ALIAS}' --sobject MsgChannelLanguageKeyword \
|
|
58
|
+
--record-id '{KEYWORD_RECORD_ID}' \
|
|
59
|
+
--values "OptInKeywords='START,YES' OptInConfirmation='You are now subscribed.'" \
|
|
60
|
+
--json > /tmp/cc-kw-write.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Check `status` in the JSON result; on non-zero return `{ok:false, kind:"keyword-record-failed", message: ...}`.
|
|
64
|
+
|
|
65
|
+
## Per-ConsentType minimum (what the readiness check requires)
|
|
66
|
+
|
|
67
|
+
The record must satisfy, for at least one language, the row matching the channel's `ConsentType` (this is the exact server-side contract from `MessagingChannelReadinessChecker` — see `gotchas.md`):
|
|
68
|
+
|
|
69
|
+
| ConsentType | Required on the keyword record |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| ImplicitOptIn | `OptOutKeywords` **and** `OptOutConfirmation` non-null |
|
|
72
|
+
| ExplicitOptIn | above **and** `OptInKeywords` non-null |
|
|
73
|
+
| DoubleOptIn | above **and** `DoubleOptInKeywords` non-null |
|
|
74
|
+
|
|
75
|
+
`OptInConfirmation` is not part of the base readiness check, but collect it — a channel that opts users in with no confirmation reply is a poor experience.
|
|
76
|
+
|
|
77
|
+
## Unified SMS extras (only when the channel is Unified SMS)
|
|
78
|
+
|
|
79
|
+
If the channel is Unified SMS (an Enhanced channel with `MessageType='Text'`; there is no `IsUnifiedMessagingEnabled` column on the Data-API `MessagingChannel` sObject — use `MessageType`), two extra rules apply to the keyword record — the readiness check enforces them:
|
|
80
|
+
|
|
81
|
+
- **Help is mandatory:** `HelpKeywords` **and** `HelpResponse` must both be non-null.
|
|
82
|
+
- **French locale custom rule:** for a `fr_CA` record, `CustomKeywords` **and** `CustomResponse` must both be non-null.
|
|
83
|
+
|
|
84
|
+
These are *not* required for non-Unified-SMS channels (WhatsApp, Facebook, etc.), where help/custom are optional. Only enforce them when you've confirmed Unified SMS in Stage 1.
|