service-de-channel-create

**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 (Te

By forcedotcom · 596 installs

npx skills add forcedotcom/sf-skills --skill service-de-channel-create

Source repository · Upstream listing

Inserting Messaging Channels Reference File Index Reference file Load when references/whatsapp.md MESSAGE TYPE=WhatsApp — WABA partnership (Stage 2) + SF side manage WABA (Stage 3) prereqs, request body, WhatsApp gotchas. references/line.md MESSAGE TYPE=Line — optional token verify, authDetails body shape, LINE gotchas. references/apple.md MESSAGE TYPE=AppleBusinessChat — empty config body, Apple precondition errors, Apple gotchas. references/facebook.md MESSAGE TYPE=Facebook — browser OAuth → page selection flow, Facebook gotchas. references/sms.md MESSAGE TYPE=Text — phone number platform key, present but unvalidated smsProvider , number provisioning prerequisite (caller's), SMS gotchas. references/connect insert.md Shared authProviderId lookup + Connect POST + response classification detail (Stage 4). references/worked examples.md End to end traces per type (fresh insert, preflight short circuit, OAuth). What this skill does Given a {MESSAGE TYPE} and its inputs, creates a MessagingChannel record with PlatformType=Enhanced , IsActive=false . The same server side path ( LiveMessageSetupServiceImpl.addChannel → LiveMessageChannelsUtil.findOrCreateCsotMessagingChannel ) also inserts a MessagingChannelUsage row ( DeploymentType=Conversation , DeploymentStatus=New on fresh insert — Disabled on a channel that was previously activated then deactivated) — we get it for free. One primary path for every type: Connect REST API. POST /services/data/v{V}/connect/livemessage/channels with an org OAuth session managed by sf api request rest . Public, sanctioned, live since API v66+. No Aura session, no token extracted into shell state. Idempotent on (MessagingPlatformKey, MessageType) — re firing with the same platform key returns the existing channel's id. This matters because MessagingChannel records are not deletable via standard means, so the preflight (Stage 1) is the practical idempotency guard. All types return the same envelope shape , so callers don't branch on type after insert: {ok, channelId, mcuId, developerName, isActive:false, messageType, messagingPlatformKey, path, created, durationMs} . Supported types (the only branch point) {MESSAGE TYPE} Platform key ( messagingPlatformKey ) Prerequisite (caller supplies) Body extras Ref WhatsApp Phone Number ID service de waba integrate (run in Stage 2 3) externalAccountId , isoCountryCode whatsapp.md Line LINE channel id LINE id + secret + token (LINE Developers Console) authDetails.{client secret,access token} line.md AppleBusinessChat Apple BC Account ID (GUID) Apple BC Account ID (register.apple.com/business chat) (none — empty config) apple.md Facebook Facebook Page ID OAuth handled internally authDetails: {} (OAuth derived) facebook.md Text Phone number / short code number already provisioned (caller supplies) smsProvider (key required, value may be "" ), isoCountryCode sms.md Unsupported (would need their own leaf logic, not yet built): WeChat , MsCopilot , Alexa . For these, emit the unsupported type envelope (Stage 0). When NOT to use this skill End to end setup. If the user wants insert → route → activate, invoke service de headless channel configure — it orchestrates this skill plus routing, consent, and activation. This skill only handles the insertion step (see Stage 0). The channel already exists and you only want to re route / activate it. Skip insertion; call service de channel routing configure / service de channel activate with the existing channelId . Inputs (from caller) {MESSAGE TYPE} — one of WhatsApp , Line , AppleBusinessChat , Facebook , Text . Case sensitive (matches the SF picklist). Text is the enum value for SMS — there is no SMS type. {ORG ALIAS} — optional; sf CLI target org alias. Default: sf config get target org . {API VERSION} — optional; Connect API version. Default: the org's current API version ( sf org display json jq r '.result.apiVersion' ). Always use the org's native version to avoid 404s. {CHANNEL NAME} — display name shown in Setup → Messaging Settings. Required for AppleBusinessChat (no phone/screen name fallback); prompted if omitted for others. Type specific inputs: WhatsApp: {WABA ID} (sent as externalAccountId ), {PHONE NUMBER ID} (the platform key — must differ from WABA ID ), {ISO COUNTRY CODE} (optional, default US ). Line: {LINE CHANNEL ID} (platform key), {LINE CHANNEL SECRET} , {LINE ACCESS TOKEN} , {VERIFY TOKEN FIRST} (optional, default true ). AppleBusinessChat: {APPLE BC ID} (platform key, GUID). Facebook: {PAGE ID} (optional — OAuth fetches/prompts if absent). Text (SMS): {SMS NUMBER} (the platform key — the phone number / short code string, already provisioned to the org), {SMS PROVIDER} (sent as smsProvider — the smsProvider key must be present in the body or the insert 400s with "SMS provider is missing", but its value is not validated or stored , so an empty string "" works; default to "" when the caller doesn't supply a provider), {ISO COUNTRY CODE} (optional, default US ). This skill does not provision the number — the caller must already know their number. Output (to caller) Success: created: false + path: "preflight" means the row already existed and was returned idempotently. Failure (shared): Type specific failure kinds ( partnership blocked , user declined , waba manage failed , meta precondition for WhatsApp; line token invalid for LINE; apple precondition for Apple; oauth not complete , oauth failed for Facebook) are documented in the per type reference files. Stage 0: Enforce orchestrator only invocation This skill is INTERNAL USE ONLY. It should only be invoked by the service de headless channel configure orchestrator. If the user's original request was to "install", "set up", "create", or "activate" a messaging channel (or any phrasing that implies end to end setup), immediately return: And render to the user: Only proceed to Stage 0.1 if: This skill was invoked programmatically by service de headless channel configure The user explicitly stated they ONLY want insertion (not routing or activation) This is a retry/resume scenario where routing/activation already succeeded When in doubt, redirect to the orchestrator. Stage 0.1: Validate type and required inputs Look up {MESSAGE TYPE} in the supported types table. If absent, emit the unsupported type envelope and return. Validate the type's required inputs are present ( Facebook is the exception — no required inputs; OAuth → page selection → prompts happen inside the Facebook flow). If any are missing, emit missing input with the list and return. WhatsApp only: additionally assert {WABA ID} != {PHONE NUMBER ID} — they are distinct Meta entities and reusing one value for both causes cryptic activation failures. If equal, emit {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."} . Capture {T0} = Date.now() for durationMs . Stage 0.2: Establish the scratch directory Every transient artifact this skill writes (preflight query results, request bodies, Connect responses) goes into a per run scratch directory rooted inside the harness working area — never a bare absolute /tmp path, which is world writable and non portable. Establish it once: The base dir is the harness provided ${outputDir} when set, else the private user scoped ${TMPDIR} , else /tmp only as a last resort. All later stages (and the reference flows) write under ${SCRATCH DIR}/ ; the durable report stays under ${outputDir} . Stage 1: Preflight — is the channel already here? SOQL probe for an existing row first ; a hit lets us skip all prerequisites and the insert. For Facebook without a {PAGE ID} , skip this stage — the page id isn't known until OAuth → page selection completes (see references/facebook.md ). Run the preflight after page selection. Two queries, not a subquery. The (SELECT ... FROM MessagingChannelUsages) child subquery fails with INVALID TYPE on orgs where the child relationship is unnameable (verified on wadtesting / PRODDEBUG). The FK keyed form works everywhere. If records.length === 1 : Record {CHANNEL ID} , {DEVELOPER NAME} , {IS ACTIVE} . Second query for the MCU by FK: Record {MCU ID} (empty MCU: flag, don't fail — the activation skill handles it). Emit the success envelope with path: "preflight", created: false . Return. If records.length 1 : ambiguous (uniqueness should prevent this) — proceed with the first, note a warning in the envelope. If records.length === 0 : proceed to Stage 2. Stage 2 3: Type specific prerequisites Most types have no prerequisite here — go straight to Stage 4. WhatsApp: requires the Meta WABA↔Salesforce partnership (Stage 2, via service de waba integrate ) and the SF side webhook subscription + credit line share (Stage 3, POST /connect/livemessage/whatsapp/business account ). Load references/whatsapp.md and follow it before inserting. Line: optional token preflight against https://api.line.me/v2/bot/info if {VERIFY TOKEN FIRST} is true. See references/line.md . Facebook: browser based OAuth authorization + page list fetch + page selection happen here, before the insert. Load references/facebook.md and follow it. Apple: none. (Apple's certificate handshake is provisioned on Apple's side and is per message at runtime, not at channel insert — see references/apple.md .) Text (SMS): none at insert. The number must already be provisioned to the org and the caller must know its smsProvider , but this skill performs no provisioning step — it inserts the record for a number the caller already has. (ESP registration runs at activation, not insert — see references/sms.md .) Stage 4: Insert via Connect REST API Shared across all types. The only per type difference is the request body ; the authProviderId lookup, the POST invocation, and the response classification table are identical. Stage 4.1: authProviderId (org global; required for every type except Text) One row → {AUTH PROVIDER ID} . Zero rows → branch on type: Text — non fatal. Text's Connect signature doesn't take authProviderId , so leave {AUTH PROVIDER ID} unset and continue to Stage 4.2; the POST omits the parameter entirely (see below). Do not emit the business error for Text — an org that has never run LiveMessage setup is exactly where the Text only path matters, and blocking here would make the advertised SMS insert unreachable on those orgs. every other type ( WhatsApp , Line , AppleBusinessChat , Facebook ) — the org has never run LiveMessage setup and the insert can't proceed; emit {ok:false, kind:"business", message:"No AuthProvider 'LiveMessageSetup' found — run the Messaging Setup wizard once on this org first"} . The Connect endpoint rejects the POST with 400 ILLEGAL QUERY PARAMETER VALUE "Missing argument authProviderId" if this URL parameter is absent — verified for every type except Text , Apple included. ( Text has its own Connect signature that doesn't require it.) When the lookup finds a row, send authProviderId for every type including Text (harmless there); when it finds none, only Text may proceed, and it proceeds without the parameter. authProviderId goes in the URL query string, never the body (body → 400 JSON PARSER ERROR "Unrecognized field 'authProviderId'" ).