@salesforce/afv-skills 1.42.0 → 1.44.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-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 +192 -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 +2 -0
- 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/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,134 @@
|
|
|
1
|
+
# Worked examples: `service-de-channel-consent-configure`
|
|
2
|
+
|
|
3
|
+
Reference runs for the three ConsentType paths. The ExplicitOptIn channel-side write, the read model, and the WhatsApp restriction below were verified live on `messaging_sdb6c` 2026-08-13.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Worked example: ImplicitOptIn (opt-out only)
|
|
8
|
+
|
|
9
|
+
Input:
|
|
10
|
+
- `{CHANNEL_ID}` = `0MjSG00000030wL0AQ` (Enhanced WhatsApp channel `DummyChannelForTesting`)
|
|
11
|
+
- `{LANGUAGE}` = `en_US`
|
|
12
|
+
|
|
13
|
+
**Stage 1** read shows `PlatformType=Enhanced`, `ConsentType=ImplicitOptIn`, and one existing `MsgChannelLanguageKeyword` (`3OrSG0000002kUz0AI`, `MasterLanguage=en_US`) with:
|
|
14
|
+
```text
|
|
15
|
+
OptOutKeywords = unsubscribe_en_US,stopall_en_US,cancel_en_US,quit_en_US,stop_en_US,end_en_US
|
|
16
|
+
OptOutConfirmation = You've opted out of receiving messages from us, so we won't contact you again.
|
|
17
|
+
```
|
|
18
|
+
That record already satisfies the ImplicitOptIn readiness rule (`OptOutKeywords` + `OptOutConfirmation` both non-null), so the skill takes the **no-op path**:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{"ok": true, "noop": true, "consentType": "ImplicitOptIn",
|
|
22
|
+
"languageKeywordIds": ["3OrSG0000002kUz0AI"],
|
|
23
|
+
"message": "Consent already configured and activation-ready"}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
If instead there were **no** keyword record, the skill would create one:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
sf data create record --sobject MsgChannelLanguageKeyword \
|
|
30
|
+
--values "MessagingChannelId=0MjSG00000030wL0AQ MasterLanguage=en_US \
|
|
31
|
+
OptOutKeywords=stop,unsubscribe,cancel OptOutConfirmation='You have been unsubscribed.'"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
and report `Success — Consent configured — channel 0MjSG00000030wL0AQ is now ImplicitOptIn with an en_US keyword record. Activation-ready.`
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Worked example: ExplicitOptIn (channel-side write verified live)
|
|
39
|
+
|
|
40
|
+
Input:
|
|
41
|
+
- `{CHANNEL_ID}` = `0MjSG0000000D9V0AU` (Enhanced Facebook channel `FACEBOOK_null_101192609043587`)
|
|
42
|
+
- `{CONSENT_TYPE}` = `ExplicitOptIn`
|
|
43
|
+
- `{LANGUAGE}` = `en_US`
|
|
44
|
+
|
|
45
|
+
**Stage 1**: `PlatformType=Enhanced`, `ConsentType=ImplicitOptIn`, `OptInPrompt=null`. Continue.
|
|
46
|
+
|
|
47
|
+
**Stage 2**: ExplicitOptIn chosen. Facebook accepts it (unlike WhatsApp rich content — see below).
|
|
48
|
+
|
|
49
|
+
**Stage 3**: collect `OptInPrompt` = `Reply YES to receive messages`.
|
|
50
|
+
|
|
51
|
+
**Stage 5 (channel PATCH)** — verified live, note the single-quoting of the multi-word prompt:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
$ sf data update record --sobject MessagingChannel --record-id 0MjSG0000000D9V0AU \
|
|
55
|
+
--values "ConsentType=ExplicitOptIn OptInPrompt='Reply YES to receive messages'"
|
|
56
|
+
# → {"id":"0MjSG0000000D9V0AU","success":true,"errors":[]}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Re-read confirmed `ConsentType=ExplicitOptIn`, `OptInPrompt=Reply YES to receive messages`.
|
|
60
|
+
|
|
61
|
+
**Stage 5 (keyword record)** — the record must also carry `OptInKeywords` (plus the ImplicitOptIn opt-out fields). Create or update the `en_US` record:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
sf data update record --sobject MsgChannelLanguageKeyword --record-id <3Or...> \
|
|
65
|
+
--values "OptInKeywords='yes,start,subscribe' OptInConfirmation='You are subscribed'"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
(Live keyword shape confirmed on sdb6c: e.g. Facebook channel `0MjSG0000000Beb0AE` carries `OptInKeywords=yes,sure` — multiple opt-in keywords as a comma-separated string.)
|
|
69
|
+
|
|
70
|
+
**Stage 6**: re-read channel + keyword record; `OptInPrompt` non-null on the channel and `OptInKeywords`+`OptOutKeywords`+`OptOutConfirmation` non-null on the keyword record → activation-ready.
|
|
71
|
+
|
|
72
|
+
**Stage 7**:
|
|
73
|
+
```json
|
|
74
|
+
{"ok": true, "consentType": "ExplicitOptIn", "channelFieldsSet": ["ConsentType","OptInPrompt"],
|
|
75
|
+
"languageKeywordId": "3Or...", "language": "en_US", "created": false, "activationReady": true}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
*(The test channel was restored afterward to `ConsentType=ImplicitOptIn, OptInPrompt=null` — passing `OptInPrompt=''` nulls the field.)*
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Worked example: DoubleOptIn
|
|
83
|
+
|
|
84
|
+
Input:
|
|
85
|
+
- `{CHANNEL_ID}` = an Enhanced Text (SMS) channel
|
|
86
|
+
- `{CONSENT_TYPE}` = `DoubleOptIn`
|
|
87
|
+
|
|
88
|
+
**Stage 3** collects both `OptInPrompt` and `DoubleOptInPrompt`.
|
|
89
|
+
|
|
90
|
+
**Stage 5 (channel PATCH)** — all three fields in one write:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
sf data update record --sobject MessagingChannel --record-id '{CHANNEL_ID}' \
|
|
94
|
+
--values "ConsentType=DoubleOptIn OptInPrompt='Reply YES to subscribe' \
|
|
95
|
+
DoubleOptInPrompt='Reply CONFIRM to confirm your subscription' IsRequireDoubleOptIn=true"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Stage 5 (keyword record)** — needs `OptInKeywords`, `DoubleOptInKeywords`, `OptOutKeywords`, `OptOutConfirmation`:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sf data update record --sobject MsgChannelLanguageKeyword --record-id <3Or...> \
|
|
102
|
+
--values "OptInKeywords=yes,start DoubleOptInKeywords=confirm \
|
|
103
|
+
OptOutKeywords=stop,unsubscribe OptOutConfirmation='You have been unsubscribed.'"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
(Live example on sdb6c: keyword record `3OrSG00000002rR0AQ` has both `OptInKeywords=yes` and `DoubleOptInKeywords=yes`, confirming DoubleOptIn uses both keyword fields.)
|
|
107
|
+
|
|
108
|
+
**Stage 6** verifies `OptInPrompt` + `DoubleOptInPrompt` non-null on the channel and `OptInKeywords` + `DoubleOptInKeywords` + opt-out fields non-null on the keyword record.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Worked example: WhatsApp rich-content rejects ExplicitOptIn (verified live)
|
|
113
|
+
|
|
114
|
+
Input:
|
|
115
|
+
- `{CHANNEL_ID}` = `0MjSG00000030wL0AQ` (Enhanced WhatsApp channel with rich content)
|
|
116
|
+
- User asks for `ExplicitOptIn`
|
|
117
|
+
|
|
118
|
+
**Stage 5** PATCH is rejected server-side:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
$ sf data update record --sobject MessagingChannel --record-id 0MjSG00000030wL0AQ \
|
|
122
|
+
--values "ConsentType=ExplicitOptIn OptInPrompt='Reply YES to receive messages'"
|
|
123
|
+
# → INVALID_INPUT: Consent Type for WhatsApp Messaging Channel with rich content
|
|
124
|
+
# cannot be set to EXPLICIT_OPT_IN(db=1,api=ExplicitOptIn).
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The skill surfaces this rather than retrying:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{"ok": false, "kind": "channel-patch-failed",
|
|
131
|
+
"message": "Consent Type for WhatsApp Messaging Channel with rich content cannot be set to EXPLICIT_OPT_IN. WhatsApp rich-content channels support ImplicitOptIn only."}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
User-facing: `Error: This WhatsApp channel has rich content and only supports ImplicitOptIn — the server rejected ExplicitOptIn. Configure opt-out keywords under ImplicitOptIn instead.`
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-create
|
|
3
|
+
description: "**INTERNAL USE ONLY — invoked by `service-de-headless-channel-configure` orchestrator.** Insert an Enhanced messaging `MessagingChannel` record (plus its child `MessagingChannelUsage` as a free server-side side-effect) of any supported type — WhatsApp, LINE, Apple Business Chat, Facebook, or SMS (Text). Given a `{MESSAGE_TYPE}` and the corresponding type-specific inputs, runs the shared preflight → Connect-insert → verify flow and branches only where a type genuinely differs (request body, prerequisites, error classes). Uses the sanctioned `POST /services/data/v{V}/connect/livemessage/channels` Connect REST endpoint — no Aura session, no token extracted into shell state. **Users should always invoke `service-de-headless-channel-configure` instead — it handles insert → route → activate in one flow.** Use this skill when inserting a messaging channel of any type; it replaces the four former per-type creation skills."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
minApiVersion: "67.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
cliTools:
|
|
9
|
+
- tool: ["curl"]
|
|
10
|
+
semver: ">=7.0.0"
|
|
11
|
+
- tool: ["jq"]
|
|
12
|
+
semver: ">=1.6.0"
|
|
13
|
+
- tool: ["sf"]
|
|
14
|
+
semver: ">=2.0.0"
|
|
15
|
+
relatedSkills:
|
|
16
|
+
- "service-de-channel-activate"
|
|
17
|
+
- "service-de-channel-consent-configure"
|
|
18
|
+
- "service-de-channel-routing-configure"
|
|
19
|
+
- "service-de-headless-channel-configure"
|
|
20
|
+
- "service-de-waba-integrate"
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Inserting Messaging Channels
|
|
24
|
+
|
|
25
|
+
## Reference File Index
|
|
26
|
+
|
|
27
|
+
| Reference file | Load when |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `references/whatsapp.md` | `MESSAGE_TYPE=WhatsApp` — WABA partnership (Stage 2) + SF-side manage-WABA (Stage 3) prereqs, request body, WhatsApp gotchas. |
|
|
30
|
+
| `references/line.md` | `MESSAGE_TYPE=Line` — optional token verify, `authDetails` body shape, LINE gotchas. |
|
|
31
|
+
| `references/apple.md` | `MESSAGE_TYPE=AppleBusinessChat` — empty-config body, Apple-precondition errors, Apple gotchas. |
|
|
32
|
+
| `references/facebook.md` | `MESSAGE_TYPE=Facebook` — browser OAuth → page-selection flow, Facebook gotchas. |
|
|
33
|
+
| `references/sms.md` | `MESSAGE_TYPE=Text` — phone-number platform key, present-but-unvalidated `smsProvider`, number-provisioning prerequisite (caller's), SMS gotchas. |
|
|
34
|
+
| `references/connect-insert.md` | Shared authProviderId lookup + Connect POST + response-classification detail (Stage 4). |
|
|
35
|
+
| `references/worked-examples.md` | End-to-end traces per type (fresh insert, preflight short-circuit, OAuth). |
|
|
36
|
+
|
|
37
|
+
## What this skill does
|
|
38
|
+
|
|
39
|
+
Given a `{MESSAGE_TYPE}` and its inputs, creates a `MessagingChannel` record with
|
|
40
|
+
`PlatformType=Enhanced`, `IsActive=false`. The same server-side path
|
|
41
|
+
(`LiveMessageSetupServiceImpl.addChannel` → `LiveMessageChannelsUtil.findOrCreateCsotMessagingChannel`)
|
|
42
|
+
also inserts a `MessagingChannelUsage` row (`DeploymentType=Conversation`, `DeploymentStatus=New`
|
|
43
|
+
on fresh insert — `Disabled` on a channel that was previously activated then deactivated) — we get
|
|
44
|
+
it for free.
|
|
45
|
+
|
|
46
|
+
**One primary path for every type: Connect REST API.**
|
|
47
|
+
`POST /services/data/v{V}/connect/livemessage/channels` with an org OAuth session managed by
|
|
48
|
+
`sf api request rest`. Public, sanctioned, live since API v66+. No Aura session, no token extracted
|
|
49
|
+
into shell state.
|
|
50
|
+
|
|
51
|
+
**Idempotent** on `(MessagingPlatformKey, MessageType)` — re-firing with the same platform key
|
|
52
|
+
returns the existing channel's id. This matters because `MessagingChannel` records are not deletable
|
|
53
|
+
via standard means, so the preflight (Stage 1) is the practical idempotency guard.
|
|
54
|
+
|
|
55
|
+
All types return the **same envelope shape**, so callers don't branch on type after insert:
|
|
56
|
+
`{ok, channelId, mcuId, developerName, isActive:false, messageType, messagingPlatformKey, path, created, durationMs}`.
|
|
57
|
+
|
|
58
|
+
### Supported types (the only branch point)
|
|
59
|
+
|
|
60
|
+
| `{MESSAGE_TYPE}` | Platform key (`messagingPlatformKey`) | Prerequisite (caller supplies) | Body extras | Ref |
|
|
61
|
+
| --- | --- | --- | --- | --- |
|
|
62
|
+
| `WhatsApp` | Phone Number ID | `service-de-waba-integrate` (run in Stage 2-3) | `externalAccountId`, `isoCountryCode` | `whatsapp.md` |
|
|
63
|
+
| `Line` | LINE channel id | LINE id + secret + token (LINE Developers Console) | `authDetails.{client_secret,access_token}` | `line.md` |
|
|
64
|
+
| `AppleBusinessChat` | Apple BC Account ID (GUID) | Apple BC Account ID (register.apple.com/business-chat) | *(none — empty config)* | `apple.md` |
|
|
65
|
+
| `Facebook` | Facebook Page ID | *OAuth handled internally* | `authDetails: {}` (OAuth-derived) | `facebook.md` |
|
|
66
|
+
| `Text` | Phone number / short code | number already provisioned (caller supplies) | `smsProvider` (key required, value may be `""`), `isoCountryCode` | `sms.md` |
|
|
67
|
+
|
|
68
|
+
Unsupported (would need their own leaf logic, not yet built): `WeChat`, `MsCopilot`, `Alexa`.
|
|
69
|
+
For these, emit the `unsupported-type` envelope (Stage 0).
|
|
70
|
+
|
|
71
|
+
## When NOT to use this skill
|
|
72
|
+
|
|
73
|
+
- **End-to-end setup.** If the user wants insert → route → activate, invoke
|
|
74
|
+
`service-de-headless-channel-configure` — it orchestrates this skill plus routing, consent, and
|
|
75
|
+
activation. This skill only handles the insertion step (see Stage 0).
|
|
76
|
+
- **The channel already exists and you only want to re-route / activate it.** Skip insertion;
|
|
77
|
+
call `service-de-channel-routing-configure` / `service-de-channel-activate` with the existing
|
|
78
|
+
`channelId`.
|
|
79
|
+
|
|
80
|
+
## Inputs (from caller)
|
|
81
|
+
|
|
82
|
+
- `{MESSAGE_TYPE}` — one of `WhatsApp`, `Line`, `AppleBusinessChat`, `Facebook`, `Text`.
|
|
83
|
+
Case-sensitive (matches the SF picklist). `Text` is the enum value for SMS — there is no `SMS` type.
|
|
84
|
+
- `{ORG_ALIAS}` — optional; `sf` CLI target-org alias. Default: `sf config get target-org`.
|
|
85
|
+
- `{API_VERSION}` — optional; Connect API version. Default: the org's current API version
|
|
86
|
+
(`sf org display --json | jq -r '.result.apiVersion'`). Always use the org's native version to
|
|
87
|
+
avoid 404s.
|
|
88
|
+
- `{CHANNEL_NAME}` — display name shown in Setup → Messaging Settings. Required for
|
|
89
|
+
`AppleBusinessChat` (no phone/screen-name fallback); prompted if omitted for others.
|
|
90
|
+
|
|
91
|
+
Type-specific inputs:
|
|
92
|
+
|
|
93
|
+
- **WhatsApp:** `{WABA_ID}` (sent as `externalAccountId`), `{PHONE_NUMBER_ID}` (the platform
|
|
94
|
+
key — **must differ from WABA_ID**), `{ISO_COUNTRY_CODE}` (optional, default `US`).
|
|
95
|
+
- **Line:** `{LINE_CHANNEL_ID}` (platform key), `{LINE_CHANNEL_SECRET}`, `{LINE_ACCESS_TOKEN}`,
|
|
96
|
+
`{VERIFY_TOKEN_FIRST}` (optional, default `true`).
|
|
97
|
+
- **AppleBusinessChat:** `{APPLE_BC_ID}` (platform key, GUID).
|
|
98
|
+
- **Facebook:** `{PAGE_ID}` (optional — OAuth fetches/prompts if absent).
|
|
99
|
+
- **Text (SMS):** `{SMS_NUMBER}` (the platform key — the phone number / short code string, already
|
|
100
|
+
provisioned to the org), `{SMS_PROVIDER}` (sent as `smsProvider` — the `smsProvider` **key must be
|
|
101
|
+
present** in the body or the insert 400s with "SMS provider is missing", but its **value is not
|
|
102
|
+
validated or stored**, so an empty string `""` works; default to `""` when the caller doesn't supply
|
|
103
|
+
a provider), `{ISO_COUNTRY_CODE}` (optional, default `US`). This skill does not provision the
|
|
104
|
+
number — the caller must already know their number.
|
|
105
|
+
|
|
106
|
+
## Output (to caller)
|
|
107
|
+
|
|
108
|
+
**Success:**
|
|
109
|
+
```json
|
|
110
|
+
{"ok": true, "channelId": "0Mj...", "mcuId": "0gL...", "developerName": "...",
|
|
111
|
+
"isActive": false, "messageType": "{MESSAGE_TYPE}", "messagingPlatformKey": "...",
|
|
112
|
+
"path": "connect" | "preflight", "created": true | false, "durationMs": 1234}
|
|
113
|
+
```
|
|
114
|
+
`created: false` + `path: "preflight"` means the row already existed and was returned idempotently.
|
|
115
|
+
|
|
116
|
+
**Failure (shared):**
|
|
117
|
+
```json
|
|
118
|
+
{"ok": false, "kind": "wrong-skill", "correctSkill": "service-de-headless-channel-configure", "hint": "..."}
|
|
119
|
+
{"ok": false, "kind": "unsupported-type", "supportedTypes": ["WhatsApp","Line","AppleBusinessChat","Facebook","Text"], "hint": "..."}
|
|
120
|
+
{"ok": false, "kind": "missing-input", "missing": ["..."], "hint": "caller must provide all required inputs for this type"}
|
|
121
|
+
{"ok": false, "kind": "auth", "hint": "OAuth token invalid / expired — run 'sf org login web'"}
|
|
122
|
+
{"ok": false, "kind": "business", "message": "...", "errorCode": "..."}
|
|
123
|
+
{"ok": false, "kind": "transport","status": 500, "message": "..."}
|
|
124
|
+
{"ok": false, "kind": "verify-failed", "hint": "Connect returned 201 but follow-up SOQL shows no matching channel"}
|
|
125
|
+
```
|
|
126
|
+
Type-specific failure kinds (`partnership-blocked`, `user-declined`, `waba-manage-failed`,
|
|
127
|
+
`meta-precondition` for WhatsApp; `line-token-invalid` for LINE; `apple-precondition` for Apple;
|
|
128
|
+
`oauth-not-complete`, `oauth-failed` for Facebook) are documented in the per-type reference files.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Stage 0: Enforce orchestrator-only invocation
|
|
133
|
+
|
|
134
|
+
**This skill is INTERNAL USE ONLY.** It should only be invoked by the
|
|
135
|
+
`service-de-headless-channel-configure` orchestrator.
|
|
136
|
+
|
|
137
|
+
If the user's original request was to "install", "set up", "create", or "activate" a messaging
|
|
138
|
+
channel (or any phrasing that implies end-to-end setup), immediately return:
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"ok": false,
|
|
143
|
+
"kind": "wrong-skill",
|
|
144
|
+
"hint": "This is an internal insertion skill. For end-to-end channel setup (insert → route → activate), invoke 'service-de-headless-channel-configure' instead with the desired MESSAGE_TYPE.",
|
|
145
|
+
"correctSkill": "service-de-headless-channel-configure"
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
And render to the user:
|
|
150
|
+
```text
|
|
151
|
+
Error: Wrong skill invoked.
|
|
152
|
+
|
|
153
|
+
This skill (service-de-channel-create) only handles the insertion step.
|
|
154
|
+
For complete channel setup, use: /service-de-headless-channel-configure
|
|
155
|
+
|
|
156
|
+
That orchestrator runs: insert → configure routing → activate in one flow.
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Only proceed to Stage 0.1 if:**
|
|
160
|
+
- This skill was invoked programmatically by `service-de-headless-channel-configure`
|
|
161
|
+
- The user explicitly stated they ONLY want insertion (not routing or activation)
|
|
162
|
+
- This is a retry/resume scenario where routing/activation already succeeded
|
|
163
|
+
|
|
164
|
+
When in doubt, redirect to the orchestrator.
|
|
165
|
+
|
|
166
|
+
### Stage 0.1: Validate type and required inputs
|
|
167
|
+
|
|
168
|
+
Look up `{MESSAGE_TYPE}` in the supported-types table. If absent, emit the `unsupported-type`
|
|
169
|
+
envelope and return.
|
|
170
|
+
|
|
171
|
+
Validate the type's required inputs are present (**Facebook is the exception** — no required
|
|
172
|
+
inputs; OAuth → page selection → prompts happen inside the Facebook flow). If any are missing,
|
|
173
|
+
emit `missing-input` with the list and return.
|
|
174
|
+
|
|
175
|
+
**WhatsApp only:** additionally assert `{WABA_ID} != {PHONE_NUMBER_ID}` — they are distinct Meta
|
|
176
|
+
entities and reusing one value for both causes cryptic activation failures. If equal, emit
|
|
177
|
+
`{ok:false, kind:"invalid-input", hint:"WABA_ID and PHONE_NUMBER_ID must differ — WABA_ID is the WhatsApp Business Account ID; PHONE_NUMBER_ID is a specific phone number within it. Find both in Meta Business Manager → WhatsApp Accounts."}`.
|
|
178
|
+
|
|
179
|
+
Capture `{T0} = Date.now()` for `durationMs`.
|
|
180
|
+
|
|
181
|
+
### Stage 0.2: Establish the scratch directory
|
|
182
|
+
|
|
183
|
+
Every transient artifact this skill writes (preflight query results, request bodies, Connect
|
|
184
|
+
responses) goes into a per-run scratch directory rooted inside the harness working area — never a
|
|
185
|
+
bare absolute `/tmp` path, which is world-writable and non-portable. Establish it once:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
SCRATCH_DIR="$(mktemp -d "${outputDir:-${TMPDIR:-/tmp}}/icc.XXXXXX")"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The base dir is the harness-provided `${outputDir}` when set, else the private user-scoped
|
|
192
|
+
`${TMPDIR}`, else `/tmp` only as a last resort. All later stages (and the reference flows) write
|
|
193
|
+
under `${SCRATCH_DIR}/`; the durable report stays under `${outputDir}`.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Stage 1: Preflight — is the channel already here?
|
|
198
|
+
|
|
199
|
+
SOQL-probe for an existing row **first**; a hit lets us skip all prerequisites and the insert.
|
|
200
|
+
|
|
201
|
+
**For Facebook without a `{PAGE_ID}`, skip this stage** — the page id isn't known until OAuth →
|
|
202
|
+
page selection completes (see `references/facebook.md`). Run the preflight after page selection.
|
|
203
|
+
|
|
204
|
+
**Two queries, not a subquery.** The `(SELECT ... FROM MessagingChannelUsages)` child subquery
|
|
205
|
+
fails with `INVALID_TYPE` on orgs where the child relationship is unnameable (verified on
|
|
206
|
+
`wadtesting` / PRODDEBUG). The FK-keyed form works everywhere.
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
210
|
+
--query "SELECT Id, DeveloperName, MasterLabel, IsActive, MessagingPlatformKey FROM MessagingChannel WHERE MessagingPlatformKey = '{PLATFORM_KEY}' AND MessageType = '{MESSAGE_TYPE}'" \
|
|
211
|
+
--json > "${SCRATCH_DIR}/preflight.json"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
If `records.length === 1`:
|
|
215
|
+
- Record `{CHANNEL_ID}`, `{DEVELOPER_NAME}`, `{IS_ACTIVE}`.
|
|
216
|
+
- Second query for the MCU by FK:
|
|
217
|
+
```bash
|
|
218
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
219
|
+
--query "SELECT Id, DeploymentStatus FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" \
|
|
220
|
+
--json > "${SCRATCH_DIR}/preflight-mcu.json"
|
|
221
|
+
```
|
|
222
|
+
Record `{MCU_ID}` (empty MCU: flag, don't fail — the activation skill handles it).
|
|
223
|
+
- Emit the success envelope with `path: "preflight", created: false`. Return.
|
|
224
|
+
|
|
225
|
+
If `records.length > 1`: ambiguous (uniqueness should prevent this) — proceed with the first,
|
|
226
|
+
note a warning in the envelope.
|
|
227
|
+
|
|
228
|
+
If `records.length === 0`: proceed to Stage 2.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Stage 2-3: Type-specific prerequisites
|
|
233
|
+
|
|
234
|
+
Most types have **no** prerequisite here — go straight to Stage 4.
|
|
235
|
+
|
|
236
|
+
- **WhatsApp:** requires the Meta WABA↔Salesforce partnership (Stage 2, via
|
|
237
|
+
`service-de-waba-integrate`) and the SF-side webhook subscription + credit-line share
|
|
238
|
+
(Stage 3, `POST /connect/livemessage/whatsapp/business-account`). **Load `references/whatsapp.md`
|
|
239
|
+
and follow it** before inserting.
|
|
240
|
+
- **Line:** optional token preflight against `https://api.line.me/v2/bot/info` if
|
|
241
|
+
`{VERIFY_TOKEN_FIRST}` is true. **See `references/line.md`.**
|
|
242
|
+
- **Facebook:** browser-based OAuth authorization + page-list fetch + page selection happen here,
|
|
243
|
+
before the insert. **Load `references/facebook.md` and follow it.**
|
|
244
|
+
- **Apple:** none. (Apple's certificate handshake is provisioned on Apple's side and is per-message
|
|
245
|
+
at runtime, not at channel insert — see `references/apple.md`.)
|
|
246
|
+
- **Text (SMS):** none at insert. The number must already be provisioned to the org and the caller
|
|
247
|
+
must know its `smsProvider`, but this skill performs no provisioning step — it inserts the record for
|
|
248
|
+
a number the caller already has. (ESP registration runs at activation, not insert — see
|
|
249
|
+
`references/sms.md`.)
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Stage 4: Insert via Connect REST API
|
|
254
|
+
|
|
255
|
+
Shared across all types. The only per-type difference is the **request body**; the authProviderId
|
|
256
|
+
lookup, the POST invocation, and the response-classification table are identical.
|
|
257
|
+
|
|
258
|
+
### Stage 4.1: authProviderId (org-global; required for every type except Text)
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
262
|
+
--query "SELECT Id FROM AuthProvider WHERE DeveloperName = 'LiveMessageSetup'" \
|
|
263
|
+
--json > "${SCRATCH_DIR}/authprovider.json"
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
One row → `{AUTH_PROVIDER_ID}`. Zero rows → **branch on type:**
|
|
267
|
+
- **`Text`** — non-fatal. Text's Connect signature doesn't take `authProviderId`, so leave
|
|
268
|
+
`{AUTH_PROVIDER_ID}` unset and continue to Stage 4.2; the POST omits the parameter entirely (see
|
|
269
|
+
below). Do **not** emit the business error for Text — an org that has never run LiveMessage setup
|
|
270
|
+
is exactly where the Text-only path matters, and blocking here would make the advertised SMS insert
|
|
271
|
+
unreachable on those orgs.
|
|
272
|
+
- **every other type** (`WhatsApp`, `Line`, `AppleBusinessChat`, `Facebook`) — the org has never run
|
|
273
|
+
LiveMessage setup and the insert can't proceed; emit
|
|
274
|
+
`{ok:false, kind:"business", message:"No AuthProvider 'LiveMessageSetup' found — run the Messaging Setup wizard once on this org first"}`.
|
|
275
|
+
|
|
276
|
+
The Connect endpoint rejects the POST with `400 ILLEGAL_QUERY_PARAMETER_VALUE "Missing argument
|
|
277
|
+
authProviderId"` if this URL parameter is absent — verified for every type **except `Text`**, Apple
|
|
278
|
+
included. (`Text` has its own Connect signature that doesn't require it.) When the lookup finds a row,
|
|
279
|
+
send `authProviderId` for every type including Text (harmless there); when it finds none, only Text may
|
|
280
|
+
proceed, and it proceeds **without** the parameter. `authProviderId` goes in the URL query string,
|
|
281
|
+
never the body (body → `400 JSON_PARSER_ERROR "Unrecognized field 'authProviderId'"`).
|
|
282
|
+
|
|
283
|
+
### Stage 4.2: Build the type-specific body and POST
|
|
284
|
+
|
|
285
|
+
Resolve `{CHANNEL_NAME}` first (prompt if required and absent — see per-type ref). Then build the
|
|
286
|
+
body from the supported-types table and POST. **Write the body to a file** (`--body @...`) — LINE
|
|
287
|
+
tokens are long and can contain shell-hostile characters.
|
|
288
|
+
|
|
289
|
+
Append the `authProviderId` query parameter only when Stage 4.1 resolved one — otherwise (the
|
|
290
|
+
Text-with-no-AuthProvider case) POST to the bare endpoint, since an empty `?authProviderId=` would
|
|
291
|
+
trip the same `ILLEGAL_QUERY_PARAMETER_VALUE`:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
URL="/services/data/v{API_VERSION}/connect/livemessage/channels"
|
|
295
|
+
[ -n "${AUTH_PROVIDER_ID:-}" ] && URL="${URL}?authProviderId=${AUTH_PROVIDER_ID}"
|
|
296
|
+
|
|
297
|
+
sf api request rest "${URL}" \
|
|
298
|
+
--method POST --target-org '{ORG_ALIAS}' \
|
|
299
|
+
--header 'Content-Type: application/json' --header 'Accept: application/json' \
|
|
300
|
+
--body @"${SCRATCH_DIR}/body.json" --include > "${SCRATCH_DIR}/connect-response.txt" 2>&1
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
`--include` prints the HTTP status/header block before the body — read the status from there, not
|
|
304
|
+
a trailing `-w` marker. `sf api request rest` manages OAuth internally.
|
|
305
|
+
|
|
306
|
+
**For the exact per-type body shape and the full response-classification table (including the
|
|
307
|
+
`DUPLICATE_VALUE` race back to Stage 1, the Facebook `AUTH_PROVIDER_NEEDS_AUTH` 401-vs-400
|
|
308
|
+
disambiguation, and per-type precondition errors), load `references/connect-insert.md` plus the
|
|
309
|
+
per-type ref.**
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Stage 5: Verify the insert
|
|
314
|
+
|
|
315
|
+
Two queries (same no-subquery reason as Stage 1). Keep to the universal field set —
|
|
316
|
+
`ExternalAccountId` and `MessagingPlatform` don't exist on all orgs.
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
320
|
+
--query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" --json
|
|
321
|
+
|
|
322
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
323
|
+
--query "SELECT Id, DeploymentStatus, DeploymentType FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" --json
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Expected: channel `IsActive=false`; exactly one MCU, `DeploymentType=Conversation`,
|
|
327
|
+
`DeploymentStatus` in `{New, Disabled}` (fresh inserts land in `New`; `Disabled` is a
|
|
328
|
+
previously-activated-then-deactivated channel — both are valid starting states for activation).
|
|
329
|
+
Empty MCU query → `{ok:false, kind:"verify-failed", hint:"Connect returned {CHANNEL_ID} but no
|
|
330
|
+
child MessagingChannelUsage was created"}`.
|
|
331
|
+
|
|
332
|
+
**Trust the DB, not the Connect response body**, for `MasterLabel` / `DeveloperName` — the 201 body
|
|
333
|
+
carries stale input-rep projections (see per-type gotchas).
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Stage 6: Report to caller
|
|
338
|
+
|
|
339
|
+
```json
|
|
340
|
+
{"ok": true, "channelId": "{CHANNEL_ID}", "mcuId": "<from Stage 5>",
|
|
341
|
+
"developerName": "<authoritative, from Stage 5>", "isActive": false,
|
|
342
|
+
"messageType": "{MESSAGE_TYPE}", "messagingPlatformKey": "{PLATFORM_KEY}",
|
|
343
|
+
"path": "connect", "created": true, "durationMs": <Date.now() - T0>}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Rendered (when invoked directly rather than by the orchestrator):
|
|
347
|
+
- `Success — MessagingChannel {CHANNEL_ID} ({developerName}) created via {path}. MCU {mcuId} auto-created. IsActive=false — run routing, then consent, then activate next.`
|
|
348
|
+
- `Info: Channel already exists — {CHANNEL_ID} ({developerName}). Proceed to routing/activation.`
|
|
349
|
+
- `Error: {kind}: {message or hint}`
|
|
350
|
+
|
|
351
|
+
On success the caller can immediately invoke `service-de-channel-routing-configure` with the
|
|
352
|
+
returned `channelId`, then `service-de-channel-consent-configure`, then
|
|
353
|
+
`service-de-channel-activate`. Those three are message-type-agnostic (they operate on
|
|
354
|
+
`MessagingChannel.Id` + `MessagingChannelUsage.Id`), so there's no downstream dispatch concern.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Gotchas (shared)
|
|
359
|
+
|
|
360
|
+
1. **Case-sensitive type names.** `"whatsapp"` ≠ `"WhatsApp"` — the SF picklist, SOQL filters,
|
|
361
|
+
and Connect enum all match exactly. Normalize upstream; don't make Stage 0.1 case-insensitive.
|
|
362
|
+
2. **Preflight is the idempotency guard.** `MessagingChannel` rows aren't deletable via standard
|
|
363
|
+
means, so re-runs rely on Stage 1 finding the existing row.
|
|
364
|
+
3. **Envelope shape is a tight coupling with callers.** If a field is renamed here, every caller
|
|
365
|
+
(especially `service-de-headless-channel-configure`) silently sees the change — break loudly
|
|
366
|
+
in tests rather than remap field names.
|
|
367
|
+
4. **Per-type gotchas live in the reference files.** WABA/phone-id confusion, LINE long-lived vs
|
|
368
|
+
short-lived tokens, Apple's empty-config body + no-provisioning-callout, Facebook OAuth page
|
|
369
|
+
selection + 401 disambiguation — load the matching `references/<type>.md` when debugging that type.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Apple Business Chat — empty config, precondition errors, gotchas
|
|
2
|
+
|
|
3
|
+
`MESSAGE_TYPE=AppleBusinessChat`. Platform key is the **Apple Business Chat Account ID** (a GUID,
|
|
4
|
+
`00000000-0000-0000-0000-000000000000`, from register.apple.com/business-chat).
|
|
5
|
+
|
|
6
|
+
Apple BC has **no third-party credentials** (no client secret, no access token). Authentication with
|
|
7
|
+
Apple's servers is certificate-based and provisioned when the Apple BC account is created on Apple's
|
|
8
|
+
Register a Business side. That's why the Setup UI's `liveMessageAppleBusinessChatHelper.js` sends
|
|
9
|
+
`messagingChannelProperties: {}` and never asks for any secret beyond the account id itself.
|
|
10
|
+
|
|
11
|
+
## Inputs
|
|
12
|
+
|
|
13
|
+
- `{APPLE_BC_ID}` — Apple Business Chat Account ID (GUID). Sent as `messagingPlatformKey`.
|
|
14
|
+
- `{CHANNEL_NAME}` — **required**. Apple BC has no phone number / screen name to fall back on, so the
|
|
15
|
+
caller must supply a display name (`messagingChannelName`). Don't synthesize `APPLE_{id}` — the
|
|
16
|
+
user sees this name in the UI.
|
|
17
|
+
|
|
18
|
+
## Stage 2-3: none
|
|
19
|
+
|
|
20
|
+
Apple has no insert-time prerequisite. The certificate handshake with Apple's servers is per-message
|
|
21
|
+
at runtime, separate from channel activation — there is no external provisioning callout at activation
|
|
22
|
+
either (Apple hits the `default` branch in the observer's external-callout switch; activation is ~1s).
|
|
23
|
+
|
|
24
|
+
## Stage 4.2: request body
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{"messageType":"AppleBusinessChat","platformType":"Enhanced",
|
|
28
|
+
"messagingPlatformKey":"{APPLE_BC_ID}",
|
|
29
|
+
"messagingChannelName":"{CHANNEL_NAME}"}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**No `authDetails` / `externalAccountId` / `isoCountryCode`.** Apple BC uses none of those; the
|
|
33
|
+
Connect input rep treats unset fields as absent (not `null`). `authProviderId` still goes in the URL
|
|
34
|
+
query string (Apple is NOT exempt — verified: omitting it yields `400 ILLEGAL_QUERY_PARAMETER_VALUE
|
|
35
|
+
"Missing argument authProviderId"`).
|
|
36
|
+
|
|
37
|
+
Response classification: same as the shared table (see `connect-insert.md`), plus — on `400` whose
|
|
38
|
+
message mentions "Apple" / "account" / "not registered" — emit
|
|
39
|
+
`{ok:false, kind:"apple-precondition", hint:"Apple BC Account ID not recognized — confirm the business is registered at register.apple.com/business-chat", message}`.
|
|
40
|
+
|
|
41
|
+
## Gotchas
|
|
42
|
+
|
|
43
|
+
1. **`messagingChannelProperties` / `authDetails` MUST be empty or omitted for Apple.** Populating
|
|
44
|
+
the map with placeholder values can make the server reject the insert with an unclear error.
|
|
45
|
+
2. **`MessagingPlatformKey` is the GUID**, not a display string. Don't paste the URL-encoded or
|
|
46
|
+
capitalized form.
|
|
47
|
+
3. **No external provisioning callout at activation** — Apple hits the `default` branch in
|
|
48
|
+
`ConversationChannelUsageDeploymentStatusService.runProvisioning`; the terminal
|
|
49
|
+
`DeploymentStatus=Active` write happens immediately (~1s, vs 15-21s for WhatsApp).
|
|
50
|
+
4. **`CHANNEL_NAME` is required** — no display phone number to fall back on.
|
|
51
|
+
5. **Apple BC onboarding is slow outside SF.** Registering at register.apple.com/business-chat can
|
|
52
|
+
take days for Apple to approve. A precondition error at insert most likely means the business
|
|
53
|
+
isn't live on Apple's side yet.
|
|
54
|
+
6. **Connect response echoes are not authoritative.** The 201 body comes back with
|
|
55
|
+
`masterLabel = messagingPlatformKey` (the GUID) and a non-canonical short `developerName`
|
|
56
|
+
(`AppleBusinessChat_<guid>`). The DB row uses the supplied `messagingChannelName` and the
|
|
57
|
+
canonical `APPLEBUSINESSCHAT_US_<guid>`. Trust Stage 5's verify SOQL, not the POST body.
|
|
58
|
+
7. **Fresh MCUs start in `DeploymentStatus=New`** (`Disabled` is the post-deactivation state). Both
|
|
59
|
+
are valid `→ Provisioning` sources — don't assert a specific starting status in Stage 5.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-de-channel-create-connect-insert
|
|
3
|
+
description: "Load when a channel's prerequisites are satisfied and CHANNEL_NAME is resolved, right before POSTing the channel-creation request to the Connect REST endpoint. Covers the shared Stage 4.1 (authProviderId lookup), Stage 4.2 (POST invocation), and Stage 4.3 (HTTP + business-error response classification, including the DUPLICATE_VALUE race). Per-type request-body shapes live in the per-type reference (whatsapp.md / line.md / apple.md / facebook.md). DO NOT load for type-specific prereqs (Stage 2-3) or for resolving CHANNEL_NAME."
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0"
|
|
6
|
+
related-skills: service-de-channel-create
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Connect insert: authProviderId, POST, response classification (shared)
|
|
10
|
+
|
|
11
|
+
Full detail for Stage 4.1-4.3 of the channel insert, shared across all four types. The per-type
|
|
12
|
+
**request body** shape is in the matching per-type reference file; everything else here is identical
|
|
13
|
+
across types.
|
|
14
|
+
|
|
15
|
+
## Stage 4.1: Get authProviderId
|
|
16
|
+
|
|
17
|
+
`sf api request rest` manages the Salesforce OAuth session internally, so there's no
|
|
18
|
+
`{ACCESS_TOKEN}`/`{INSTANCE_URL}` to carry in shell state.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
sf data query --target-org '{ORG_ALIAS}' \
|
|
22
|
+
--query "SELECT Id FROM AuthProvider WHERE DeveloperName = 'LiveMessageSetup'" \
|
|
23
|
+
--json > "${SCRATCH_DIR}/authprovider.json"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- `records.length === 1` → record `{AUTH_PROVIDER_ID} = records[0].Id`.
|
|
27
|
+
- `records.length === 0` → `{ok:false, kind:"business", message:"No AuthProvider 'LiveMessageSetup' found — run the Messaging Setup wizard once on this org first"}`.
|
|
28
|
+
|
|
29
|
+
`authProviderId` is **org-global** — the same `LiveMessageSetup` `AuthProvider.Id` works for every
|
|
30
|
+
message type on that org, so a caller inserting multiple channels can reuse it. The value differs on
|
|
31
|
+
every org; always query, never hardcode.
|
|
32
|
+
|
|
33
|
+
## Stage 4.2: POST to the Connect endpoint
|
|
34
|
+
|
|
35
|
+
Build the type-specific body (see the per-type reference), **write it to a file** under
|
|
36
|
+
`${SCRATCH_DIR}` (the per-run scratch dir established in SKILL.md Stage 0.2 — under
|
|
37
|
+
`${outputDir}`/`${TMPDIR}`, never a bare `/tmp` path), then POST:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
sf api request rest \
|
|
41
|
+
"/services/data/v{API_VERSION}/connect/livemessage/channels?authProviderId=${AUTH_PROVIDER_ID}" \
|
|
42
|
+
--method POST --target-org '{ORG_ALIAS}' \
|
|
43
|
+
--header 'Content-Type: application/json' --header 'Accept: application/json' \
|
|
44
|
+
--body @"${SCRATCH_DIR}/body.json" --include > "${SCRATCH_DIR}/connect-response.txt" 2>&1
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**`authProviderId` is a URL query-string parameter, NOT a body field** — mandatory on every known org
|
|
48
|
+
and every type (Apple included). Omitting it from the URL → `400 ILLEGAL_QUERY_PARAMETER_VALUE
|
|
49
|
+
"Missing argument authProviderId"`. Putting it in the body → `400 JSON_PARSER_ERROR "Unrecognized
|
|
50
|
+
field 'authProviderId'"`. The only working form is `?authProviderId=${AUTH_PROVIDER_ID}` in the URL.
|
|
51
|
+
|
|
52
|
+
**`sf api request rest` manages the Salesforce OAuth session internally** — no bearer token is
|
|
53
|
+
extracted into shell state. `--include` prints the HTTP status/headers block ahead of the body — read
|
|
54
|
+
the status from that block, not a trailing `-w` marker.
|
|
55
|
+
|
|
56
|
+
## Stage 4.3: Classify the Connect response
|
|
57
|
+
|
|
58
|
+
**Classify by `errorCode` first, HTTP status second** — the endpoint is inconsistent about which
|
|
59
|
+
status it pairs with some error codes (notably Facebook's `AUTH_PROVIDER_NEEDS_AUTH`). Inspect
|
|
60
|
+
`response[0].errorCode` (or `.errorCode` if the body is an object, not an array) before falling back
|
|
61
|
+
to the status table.
|
|
62
|
+
|
|
63
|
+
| HTTP status | Body shape | Handling |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| 201 | `{id, developerName, isActive:false, ...}` | Success. Record `{CHANNEL_ID}=id`, `{DEVELOPER_NAME}=developerName`. Continue to Stage 5. |
|
|
66
|
+
| 200 | Same shape | Idempotent hit on some orgs. Treat same as 201. |
|
|
67
|
+
| 400 | `[{errorCode, message}]` | Business-level rejection. See table below. |
|
|
68
|
+
| 401 | empty / `INVALID_SESSION_ID` | SF bearer token invalid. `{ok:false, kind:"auth", hint:"OAuth token invalid — run 'sf org login web'"}`. |
|
|
69
|
+
| 403 | `[{errorCode:"INSUFFICIENT_ACCESS"}]` | `{ok:false, kind:"business", errorCode:"INSUFFICIENT_ACCESS", message}`. |
|
|
70
|
+
| 404 | usually HTML | Endpoint unavailable / API version unsupported. `{ok:false, kind:"transport", status:404, message:"Connect endpoint unavailable — is {API_VERSION} supported?"}`. |
|
|
71
|
+
| 5xx | varies | `{ok:false, kind:"transport", status, message}`. |
|
|
72
|
+
|
|
73
|
+
**Business errors (HTTP 400, or any status when classified by errorCode):**
|
|
74
|
+
|
|
75
|
+
| `errorCode` | Meaning | Handling |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `DUPLICATE_VALUE` | Channel already exists (race — preflight missed it). | Re-run Stage 1 preflight; if a row now exists, emit success `created:false, path:"connect-duplicate"`. |
|
|
78
|
+
| `INVALID_INPUT` / `MALFORMED_REQUEST` | Body shape issue — likely a skill bug. | `{ok:false, kind:"business", errorCode, message}`; report the request body for debugging. |
|
|
79
|
+
| `AUTH_PROVIDER_NEEDS_AUTH` (Facebook) | OAuth not complete / token expired. | `{ok:false, kind:"oauth-not-complete", hint:"re-run Facebook OAuth to refresh the FB token"}`. |
|
|
80
|
+
| `INVALID_STATUS` / `UNKNOWN_EXCEPTION` mentioning "WABA"/"Meta" | Meta-side precondition failed. | `{ok:false, kind:"meta-precondition", hint:"WABA may not be shared with SF's BSP app, or the phone may not be OTP-verified on Meta", message}`. |
|
|
81
|
+
| any other | Unrecognized. | `{ok:false, kind:"business", errorCode, message}`. |
|
|
82
|
+
|
|
83
|
+
Per-type message-substring refinements (LINE creds, Apple precondition) are in the per-type reference
|
|
84
|
+
files. Return the classified envelope.
|