service-de-headless-channel-configure
Top-level orchestrator: given a messaging channel type and its type-specific inputs, produces an activated Enhanced `MessagingChannel` in the target Salesforce org — without the Meta/LINE/Apple setup popups. Prompts for `{MESSAGE_TYPE}` if not supplied, then sequences insert → route → consent → acti
By forcedotcom · 598 installs
npx skills add forcedotcom/sf-skills --skill service-de-headless-channel-configure
Source repository · Upstream listing
Headless Channel Setup (type agnostic)
What this skill does
Takes a channel type ( WhatsApp Line AppleBusinessChat Facebook Text ) plus its type specific inputs and drives it to an activated MessagingChannel . It first gates on the Enhanced Messaging Terms and Conditions, then runs four steps:
0. Accept Terms and Conditions — renders the Enhanced Messaging disclaimer and requires the user to certify they have authority to bind their org, mirroring the in org "Add a Channel" wizard. Blocking — a decline ends the run before any org work. (Stage 0.5, in this skill)
1. Insert the channel — invokes service de channel create , which handles the per type third party prereq internally (e.g. WhatsApp's service de waba integrate runs inside service de channel create ). (skill: service de channel create )
2. Configure routing — sets SessionHandlerId to an Omni Channel Queue (pick existing or create new). Message type agnostic. (skill: service de channel routing configure )
3. Configure consent — ensures a valid ConsentType + matching MsgChannelLanguageKeyword record. The Connect insert auto seeds a default ConsentType=ImplicitOptIn + opt out keyword, so on a fresh channel this step is frequently a no op — or an upgrade if the caller wants ExplicitOptIn/DoubleOptIn. Activation's readiness check requires both routing AND consent, so this runs before activation. (skill: service de channel consent configure )
4. Activate — PATCHes MessagingChannelUsage.DeploymentStatus to Provisioning via REST; the server side save hook drives the full observer chain synchronously and returns 204 once the MCU reaches Active and MessagingChannel.IsActive=true . Message type agnostic. (skill: service de channel activate )
Resume by default. Each step is idempotent — if you re run after a failure, the orchestrator detects existing state and skips steps that already succeeded.
Prompt mid flow. If {MESSAGE TYPE} isn't in the initial request, the orchestrator asks upfront. Routing choices (pick queue / create new) are prompted when step 2 runs, not upfront — keeps leaf skills self contained.
Reference File Index
Reference file Load when
references/inputs.md You need the full input field list — MESSAGE TYPE/ORG ALIAS plus per type fields for WhatsApp, LINE, AppleBusinessChat, Facebook, Text (SMS).
references/terms and conditions.md Rendering the Stage 0.5 disclaimer and certification prompt — verbatim text and per type label/third party mappings.
references/output envelopes.md Wiring error handling or parsing the canonical success/failure/terms declined JSON envelope shapes.
references/partial success.md The orchestrator fails mid way and you need to render the partial success/resume report.
references/gotchas.md Debugging unexpected behavior — duplicate key errors, resume/idempotency questions, preflight SOQL shape, unattended/CI usage.
references/worked examples.md You need a reference trace (fresh WhatsApp run, resume after failure, Apple dispatch, unsupported type rejection) for exact envelope shapes.
Critical execution rule
This orchestrator MUST run all stages (0 5) in a SINGLE response without stopping between stages.
When you invoke a leaf skill (service de channel create, service de channel routing configure, service de channel consent configure, service de channel activate) and it returns via function results:
DO: Immediately parse the function results return value and continue to the next stage
DO: Run Stage 0.5 → Stage 1 → Stage 2 → Stage 3 → Stage 3.5 → Stage 4 → Stage 5 consecutively in ONE response
DO NOT: Stop and report back to the user between stages
DO NOT: Wait for user confirmation between stages
DO NOT: Treat each stage as a separate turn in the conversation
Why this matters: The user invoked THIS orchestrator skill to get an end to end activated channel. They did NOT invoke the individual leaf skills. Your job is to run the entire pipeline autonomously and report the final result. Stopping mid flow breaks the orchestrator pattern and forces the user to manually resume each stage.
Exception: Only stop mid flow if a leaf skill returns ok: false with a terminal error (not a recoverable prompt), or if the user declines the Stage 0.5 Terms and Conditions gate (emit terms not accepted and halt). Then report the failure immediately with context about which stage failed and what state was reached.
Stage 0.5 is a required blocking prompt. The Terms and Conditions gate is the one prompt that must be answered affirmatively before any org work. It is not optional and must not be auto accepted. A no /decline ends the run per the exception above; a yes continues to Stage 1 in the same response.
Leaf skill prompts: Some leaf skills (like service de waba integrate or service de channel routing configure ) may prompt the user for input mid execution. That's fine — answer those prompts as they come up, but then CONTINUE to the next stage immediately after the prompt is answered. Don't stop just because a prompt was involved.
When NOT to use this skill
You only want one step. Invoke the leaf skill directly ( service de channel create , service de channel routing configure , service de channel activate ). This orchestrator is for the full automated path from nothing → activated channel.
You want to run stages independently in separate conversation turns. This orchestrator runs all stages consecutively in one response. If you need manual control between stages, invoke the leaf skills individually.
You want a message type outside WhatsApp / Line / AppleBusinessChat / Facebook / Text . The dispatcher will return unsupported type . WeChat / MsCopilot / Alexa would need their own leaves + a dispatcher update.
The prereq hasn't been met (WABA not shared, LINE channel not created, Apple account not registered, SMS number not provisioned). The per type insert skill's Stage 2 equivalent surfaces this clearly and returns; no orchestration fixes it. (For SMS/Text the number provisioning prereq isn't checked at insert — an unprovisioned number surfaces later as an activation time provisioning error .)
Inputs (from user)
{MESSAGE TYPE} (prompted if omitted) and {ORG ALIAS} (optional), plus type specific fields for WhatsApp, LINE, AppleBusinessChat, Facebook, and Text (SMS) forwarded to the dispatcher. Load references/inputs.md and follow it for the full field list and the omitted type prompt text.
Output (to user)
Three top level outcomes: success, step annotated failure, and terms declined. Load references/output envelopes.md and follow it for the exact JSON shapes.
Stage 0: Resolve {MESSAGE TYPE} and type specific key
If {MESSAGE TYPE} is omitted, prompt the user (see "Inputs" above). Refuse to proceed without one — there's no sensible default.
Once known, determine the preflight key — the field we'll filter the MessagingChannel SOQL on to detect existing state. It's always MessagingPlatformKey , but what that value is varies by type:
{MESSAGE TYPE} Preflight key value {PLATFORM KEY} Notes
WhatsApp {PHONE NUMBER ID} Required upfront
Line {LINE CHANNEL ID} Required upfront
AppleBusinessChat {APPLE BC ID} Required upfront
Facebook {PAGE ID} Optional OAuth → fetch pages → prompt if not provided
Text {SMS NUMBER} Required upfront (the phone number / short code itself)
Facebook is unique: If {PAGE ID} is not provided, the orchestrator cannot run its own Stage 1 preflight. Instead, skip Stage 1 and let service de channel create handle the full OAuth → page selection → preflight → creation flow. The insertion skill has its own Stage 1 preflight that runs after page selection.
For non Facebook types, validate that the caller provided the required type specific inputs for {MESSAGE TYPE} (see Inputs above). If missing, prompt or halt — don't call the dispatcher, it will re validate and return missing input , wasting a round trip.
Stage 0.5: Enhanced Messaging Terms and Conditions (blocking gate)
Run this before any org query or channel work. The in org "Add a Channel" wizard requires the admin to accept the Enhanced Messaging Terms and Conditions before it will let them proceed to channel setup. The headless flow must enforce the same gate — do not skip it, do not accept on the user's behalf. This runs once per orchestrator invocation, after {MESSAGE TYPE} is known (the disclaimer names the channel) and before Stage 1.
Render the disclaimer and certification prompt from references/terms and conditions.md (verbatim, substituting the channel label) — load it and follow it for the exact wording and per type label/third party mappings.
Gate behavior:
If the user answers yes (affirmative acceptance), record {TC ACCEPTED} = true and continue to Stage 1.
If the user answers no , declines, or does not affirmatively accept, halt immediately — do not run Stage 1 or any subsequent stage. Emit:
Acceptance is required on every orchestrator invocation, including resume runs. It is a per session gate, not persisted — we don't have a place to durably record it, and the cost of re confirming is one prompt.
Stage 1: Pre flight state detection (resume support)
Skip this stage entirely if MESSAGE TYPE=Facebook and {PAGE ID} was not provided. The Facebook insertion skill needs to run OAuth first before we know which PAGE ID to check for. Let the insertion skill handle its own preflight in Stage 1 after page selection.
For all other cases, query the org to see how far a prior run got. This enables skip ahead on resume. Two queries, not a subquery — the (SELECT ... FROM MessagingChannelUsages) subquery fails on some orgs where the child relationship is unnameable. Use FK keyed second query instead (see gotcha 9).
If the channel query returns a row, fire a second query to resolve its MCU:
Parse the combined result to figure out {CURRENT STATE} :
Preflight result CURRENT STATE First step to run
No records fresh Step 1 (insert)
Record exists, IsActive=false , SessionHandlerId=null and FallbackQueueId=null inserted no routing Step 2 (configure routing)
Record exists, IsActive=false , routing set routed not active Step 3 (configure consent), then Step 4 (activate)
Record exists, IsActive=true already active Emit no op success envelope; return.
Record the existing {CHANNEL ID} and {MCU ID} if present — downstream steps need them.
Initialize {STEPS RUN} = [] , {STEPS SKIPPED} = [] , {T0} = Date.now() .
Stage 2: Insert the channel (step 1 of 4)
Skip this stage if CURRENT STATE ∈ {inserted no routing, routed not active, already active} — the channel already exists. Append "insert" to STEPS SKIPPED in that case.
Otherwise invoke service de channel create with:
{MESSAGE TYPE} = (from Stage 0)
All type specific inputs (forwarded verbatim — dispatcher routes to the right leaf)
{ORG ALIAS}
The insertion skill handles per type prereqs internally (e.g., for WhatsApp, service de channel create invokes service de waba integrate as its Stage 2). This orchestrator doesn't need to know about WABA linking, LINE token verification, or Apple registration — service de channel create owns the prereq semantics for whichever type it's running.
Handle the envelope:
Leaf envelope Orchestrator action
{ok: true, channelId, mcuId, ...} Record {CHANNEL ID} and {MCU ID} . Append "insert" to STEPS RUN . Continue.
{ok: false, kind: "partnership blocked" \ "user declined" \ "apple registration pending" \ "line prereq missing"} These come from the per type prereq stage. Pass through as failedSte