Security And Hardening
penpot/penpot
Hardens code against vulnerabilities. An agent skill from penpot/penpot.
Activate an Enhanced MessagingChannel (WhatsApp/Apple/Facebook/SMS/RCS) by PATCHing MessagingChannelUsage.DeploymentStatus from Disabled to Provisioning via the REST sobject endpoint.
$ npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install forcedotcom/sf-skills service-de-channel-activate --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/forcedotcom/sf-skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/service-de-channel-activate .claude/skills/service-de-channel-activate && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "service-de-channel-activate" agent skill from https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activate into .claude/skills/service-de-channel-activate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "service-de-channel-activate", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activateType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install forcedotcom/sf-skills service-de-channel-activate --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/forcedotcom/sf-skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/service-de-channel-activate .agents/skills/service-de-channel-activate && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "service-de-channel-activate" agent skill from https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activate into .agents/skills/service-de-channel-activate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "service-de-channel-activate", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install forcedotcom/sf-skills service-de-channel-activate --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/forcedotcom/sf-skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/service-de-channel-activate .cursor/skills/service-de-channel-activate && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "service-de-channel-activate" agent skill from https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activate into .cursor/skills/service-de-channel-activate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "service-de-channel-activate", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/forcedotcom/sf-skills.git --path skills/service-de-channel-activate--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install forcedotcom/sf-skills service-de-channel-activate --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/forcedotcom/sf-skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/service-de-channel-activate .gemini/skills/service-de-channel-activate && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "service-de-channel-activate" agent skill from https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activate into .gemini/skills/service-de-channel-activate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "service-de-channel-activate", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install forcedotcom/sf-skills service-de-channel-activateInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/forcedotcom/sf-skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/service-de-channel-activate .github/skills/service-de-channel-activate && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "service-de-channel-activate" agent skill from https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activate into .github/skills/service-de-channel-activate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "service-de-channel-activate", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install forcedotcom/sf-skills service-de-channel-activate --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/forcedotcom/sf-skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/service-de-channel-activate .opencode/skills/service-de-channel-activate && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "service-de-channel-activate" agent skill from https://github.com/forcedotcom/sf-skills/tree/main/skills/service-de-channel-activate into .opencode/skills/service-de-channel-activate/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "service-de-channel-activate", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
service-de-channel-activateActivate an Enhanced MessagingChannel (WhatsApp/Apple/Facebook/SMS/RCS) by PATCHing MessagingChannelUsage.DeploymentStatus from Disabled to Provisioning via the REST sobject endpoint.
Service De Channel Activate is an agent skill from forcedotcom/sf-skills. Activate an Enhanced MessagingChannel (WhatsApp/Apple/Facebook/SMS/RCS) by PATCHing MessagingChannelUsage.DeploymentStatus from Disabled to Provisioning via the REST sobject endpoint. The UDD save-hook dispatches by MessageType for the external callout (WHATSAPP→Meta /register, FACEBOOK→subscribeFacebookPage, TEXT→registerCsotSms; Apple/LINE need none), writes the terminal Active/Error, and flips IsActive. Synchronous on the HTTP response — WhatsApp returns 204 after Meta confirms (~15-21s); no-callout channels…
Its SKILL.md is about 4.9k tokens, which your agent loads only when the skill is triggered. The skill folder holds 4 other files, including reference files (for example `references/gotchas.md`, `references/phone-verification.md` and `references/worked-examples.md`).
It sits in Security, covering Web application vulnerabilities. It works with WhatsApp. The repository describes itself as: Salesforce's curated collection of agent skills for building applications. Optimized for Agentforce Vibes, compatible with all AI tools. The licence is Apache-2.0.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 4bbae5c. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
sfnodeFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
MESSAGING_PLATFORM_KEYFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Service De Channel Activate loads about 4.9k tokens when it runs, and up to ~8.2k if it reads all its reference files. Until then it costs about 195 tokens; SKILL.md has 1,682 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from forcedotcom/sf-skills at commit 4bbae5c, republished under its Apache-2.0 licence (© forcedotcom). 1,682 words, ~4,905 tokens.
.claude/skills/service-de-channel-activate/SKILL.md (or your agent's skills folder). This skill also uses 3 other files; get the full folder from GitHub.Given a {CHANNEL_ID}, reads the channel's MessagingChannelUsage.Id, then fires PATCH /services/data/v{V}/sobjects/MessagingChannelUsage/{MCU_ID} with body {"DeploymentStatus":"Provisioning"}. The server-side chain:
MessagingChannelUsageFunctions.validateBeforeSave runs validateDeploymentStatus → validateChannelReadinessOnProvisioning. For WhatsApp this confirms consent is configured. Rejected writes return HTTP 400 FIELD_INTEGRITY_EXCEPTION.MessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnce fires unconditionally after the UPDATE statement. It calls MessagingChannelUsageFunctionsHelper.handlePostSave which registers a post-commit TransactionObserver.ConversationChannelUsageDeploymentStatusService.handleProvisioning (inherited from AbstractChannelUsageDeploymentStatusService), which:runProvisioning — switch-dispatches by MessageType for the external callout:WHATS_APP → registerCsotWhatsAppNumber → LiveMessageSetupApi.registerWhatsAppNumber → Meta /register + status verification. 15-21s wall-clock.FACEBOOK → metaGraphApiService.subscribeFacebookPage.TEXT → registerCsotSms.AppleBusinessChat, Line, everything else → default branch, no external callout, no network wait.DeploymentStatus = 'Active' via PLSQL.DeploymentStatus = 'Error' plus ErrorReason / ErrorDetails.MessagingChannel.IsActive = true once MCU reaches Active (the isTransitioningStatus flag skips the flip while status is still Provisioning).All synchronous within the PATCH request — the 204 response only comes back after the full chain completes. WhatsApp: ~15-21s (Meta /register round-trip). Apple / Line: ~1s (no external call; just the local save-hook + observer + PLSQL write). Verified on wadtesting 2026-04-30.
| Reference file | Load when |
|---|---|
references/phone-verification.md | Stage 3 comes back with ErrorReason === "VERIFICATION_REQUIRED" (WhatsApp only) — the phone-number OTP verification sub-flow. |
references/worked-examples.md | You want a reference run of the WhatsApp happy-path, Apple activation, a readiness failure, or the already-active no-op. |
references/gotchas.md | Troubleshooting an unexpected result, or before modifying this skill — the eleven known gotchas. |
A direct REST PATCH produces the identical save-hook chain as the old activateChannelUsage Apex method, with substantially less machinery — no CSRF cookie acquisition, no bootstrap fetch, no Aura response parsing, no double-wrapped returnValue. REST semantics are honest: 204 means the transition succeeded; 4xx means it didn't.
Code proof: MessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnce fires on any DML path (REST, SOAP, Apex, Metadata API) — there is no Apex-specific gate. The entity XML (MessagingChannelUsage.entity.xml) marks DeploymentStatus as editAccess="always" with no <readonly> attribute. The transition validator (getValidAPIStatusTransitions) allows Disabled → Provisioning (and New → Provisioning, and Error → Provisioning | Deprovisioning, and Active → Deprovisioning). The DB-only transitions Provisioning → Active | Error are reserved for the observer's PLSQL call — that's why we write Provisioning and let the server pick the terminal state.
IsActive=true. Re-firing is blocked by the API transition validator (Active → Provisioning is not in getValidAPIStatusTransitions()) — the PATCH would return 400. The Stage 1 precondition check catches this and emits noop:true.activateChannelUsage used to fail with LiveMessageSetupException / nullQueueId at the Apex entry point. With the REST path the same guard lives in validateChannelReadinessOnProvisioning — write with SessionHandlerId=null && FallbackQueueId=null → 400 FIELD_INTEGRITY_EXCEPTION. Run service-de-channel-routing-configure first. The Stage 1 check still runs defensively.addChannel.{CHANNEL_ID} — a 15- or 18-char MessagingChannel.Id (prefix 0Mj). The channel must already exist with a non-null SessionHandlerId or FallbackQueueId.{ORG_ALIAS} — optional; the sf CLI target-org alias. Default: whatever sf config get target-org returns. Used for OAuth and SOQL reads.{API_VERSION} — optional; REST API version. Default: 68.0. Any version where MessagingChannelUsage is addressable as a standard sobject is fine (v50+ should work; not exhaustively tested).Unlike the old Aura-based version of this skill, there are no {POLL_TIMEOUT_S} / {POLL_INTERVAL_S} inputs — the PATCH is synchronous end-to-end.
Success — channel is live:
{"ok": true, "channelId": "0Mj...", "mcuId": "0gL...", "deploymentStatus": "Active", "isActive": true, "messageType": "WhatsApp", "durationMs": 20934}Success — no-op (already active):
{"ok": true, "noop": true, "channelId": "0Mj...", "message": "Channel already active"}Failure — precondition not met:
{"ok": false, "kind": "no-routing", "hint": "run service-de-channel-routing-configure first"}
{"ok": false, "kind": "no-mcu", "hint": "no MessagingChannelUsage row for this channel — run the insertion skill first"}
{"ok": false, "kind": "channel-missing","hint": "MessagingChannel id not found"}Failure — validator or server-side provisioning error:
{"ok": false, "kind": "readiness-failed", "errorCode": "FIELD_INTEGRITY_EXCEPTION",
"message": "...", "hint": "validateChannelReadinessOnProvisioning rejected the write — most commonly missing consent; run service-de-channel-settings-configure first"}
{"ok": false, "kind": "provisioning-error", "mcuId": "0gL...", "errorReason": "MetaRegistrationFailed", "errorDetails": "..."}
{"ok": false, "kind": "verification-failed", "hint": "WhatsApp phone number verification failed or was declined by user"}
{"ok": false, "kind": "verification-request-failed", "errorCode": "...", "message": "...", "hint": "Could not request verification code from Meta"}Failure — auth / transport:
{"ok": false, "kind": "auth", "hint": "OAuth token invalid / expired — run 'sf org login web'"}
{"ok": false, "kind": "transport", "status": 500, "message": "..."}Read the channel, then its MCU, as two separate SOQL calls. A combined subquery would be cheaper but fails on orgs where the child relationship is unnameable (see gotcha #4).
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeveloperName, MessageType, IsActive, SessionHandlerId, FallbackQueueId, MessagingPlatformKey FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" \
--json > /tmp/amc-precheck-channel.json
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeploymentStatus, DeploymentType, ErrorReason, ErrorDetails FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" \
--json > /tmp/amc-precheck-mcu.jsonLet channel = /tmp/amc-precheck-channel.json records[0] and mcu = /tmp/amc-precheck-mcu.json records[0]:
| Condition | Envelope |
|---|---|
| Channel query returned 0 records | {ok:false, kind:"channel-missing", hint:"MessagingChannel id not found"} |
channel.IsActive === true | {ok:true, noop:true, channelId, message:"Channel already active"} — return |
channel.SessionHandlerId == null && channel.FallbackQueueId == null | {ok:false, kind:"no-routing", hint:"run service-de-channel-routing-configure first"} |
| MCU query returned 0 records | {ok:false, kind:"no-mcu", hint:"no MessagingChannelUsage row for this channel — run the insertion skill first"} |
| Otherwise | Record {MCU_ID} = mcu.Id, {MESSAGE_TYPE} = channel.MessageType, {MESSAGING_PLATFORM_KEY} = channel.MessagingPlatformKey, {INITIAL_MCU_STATUS} = mcu.DeploymentStatus and continue to Stage 2. |
Also record {T0} (epoch ms at start of Stage 2) so the final envelope can report durationMs.
If {INITIAL_MCU_STATUS} === "Provisioning" — the MCU is already mid-flight from a prior call in this transaction window. Skip Stage 2 (firing the PATCH) entirely and jump to Stage 3 (verification). This is a rare race guard: the observer is synchronous with the PATCH, so by the time the caller sees the 204 the status is already terminal (Active or Error) — Provisioning should be invisible from outside. If we do see it in the precheck, something wrote Provisioning in a separate DML and the observer is still mid-flight — don't fire a second PATCH.
MessagingChannelUsage.DeploymentStatus = "Provisioning"Use sf api request rest so authentication stays inside the CLI's transport — no OAuth token is ever extracted into shell state.
Fire the PATCH. This call can take 15-30 seconds for WhatsApp — the observer runs synchronously, including Meta's /register round trip. sf api request rest has no separate client-side timeout to raise; it waits on the underlying HTTP call.
sf api request rest \
"/services/data/v{API_VERSION}/sobjects/MessagingChannelUsage/{MCU_ID}" \
--method PATCH \
--target-org '{ORG_ALIAS}' \
--header 'Content-Type: application/json' \
--body '{"DeploymentStatus":"Provisioning"}' \
--include \
> /tmp/amc-patch-response.txt 2>&1
HTTP_CODE=$(head -1 /tmp/amc-patch-response.txt | grep -oE '[0-9]{3}')--include prints the HTTP status/headers block ahead of the (typically empty, on 204) body — read the status line from that block rather than a -w-style trailing marker.
Classify by HTTP status:
| Status | Body | Handling |
|---|---|---|
| 204 | empty | Success. The observer ran to completion; MCU is Active or Error. Continue to Stage 3 to read the terminal state. |
| 400 | [{errorCode:"FIELD_INTEGRITY_EXCEPTION", message:"..."}] | Validator rejection. See table below. |
| 400 | [{errorCode:"INVALID_FIELD_FOR_INSERT_UPDATE" or "MALFORMED_ID", ...}] | Skill bug — the MCU_ID from Stage 1 was wrong, or the body shape is off. Emit {ok:false, kind:"transport", status:400, message}. |
| 401 | (usually empty) | Bearer token invalid. Emit {ok:false, kind:"auth", hint:"OAuth token invalid / expired — run 'sf org login web'"}. |
| 403 | [{errorCode:"INSUFFICIENT_ACCESS"}] | User lacks perm to write DeploymentStatus. Emit {ok:false, kind:"business", errorCode:"INSUFFICIENT_ACCESS", message}. |
| 5xx | varies | Transport. The observer may have partially committed — Stage 3's SOQL is the source of truth. Read MCU state; if it's Active, report success with a warning; if Error or still Disabled, classify as {ok:false, kind:"transport", status, message}. |
Known 400 FIELD_INTEGRITY_EXCEPTION messages:
| Message fragment | Meaning | Envelope |
|---|---|---|
| "invalid deployment status transition" | The current status doesn't allow → Provisioning (e.g. MCU is already Active — Stage 1 should have caught this, but there's a race window). | Re-read MCU; if now Active, emit success-noop. If Provisioning, Stage 3 poll. Otherwise emit {ok:false, kind:"readiness-failed", ...}. |
| "consent" / "keyword" / mentions of STOP/HELP | validateChannelReadinessOnProvisioning rejected — channel doesn't have required consent configured. | {ok:false, kind:"readiness-failed", errorCode:"FIELD_INTEGRITY_EXCEPTION", message, hint:"channel requires a ConsentType and a matching MsgChannelLanguageKeyword record (opt-out keyword + confirmation) before activation — run service-de-channel-settings-configure"}. |
| "routing" / "queue" / "SessionHandler" | Routing precondition (Stage 1 should have caught, but the validator re-checks). | {ok:false, kind:"no-routing", message, hint:"run service-de-channel-routing-configure first"}. |
| other | Unrecognized validator error. | {ok:false, kind:"readiness-failed", errorCode, message}. |
The PATCH is synchronous, so by the time we're here the MCU is Active or Error — no polling. Read once:
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeploymentStatus, ErrorReason, ErrorDetails FROM MessagingChannelUsage WHERE Id = '{MCU_ID}'" \
--json > /tmp/amc-poststate-mcu.json| Status | Handling |
|---|---|
Active | Continue to Stage 4. |
Error with ErrorReason === "VERIFICATION_REQUIRED" | For WhatsApp channels only: phone number needs OTP verification. Continue to Stage 3.2 (WhatsApp verification flow). |
Error (other) | Emit {ok:false, kind:"provisioning-error", mcuId, errorReason, errorDetails}. |
Provisioning | Unexpected — the observer was supposed to terminate before the PATCH returned 204. Fall through to a defensive poll (see Stage 3.1). |
Disabled | The PATCH returned 204 but the write didn't take? Emit {ok:false, kind:"transport", message:"PATCH returned 204 but MCU is still Disabled — observer didn't commit"}. |
This sub-flow only runs for WhatsApp channels when activation fails with ErrorReason === "VERIFICATION_REQUIRED" — the phone number needs OTP verification with Meta before it can be registered. If the MCU comes back with ErrorReason === "VERIFICATION_REQUIRED" (WhatsApp only), load references/phone-verification.md and follow it to drive the phone-number verification sub-flow (request code → prompt user → validate code → retry activation once).
Provisioning)The observer's external-callout block (WhatsApp/Facebook/SMS) runs synchronously inside the PATCH request — there's no async queue indirection in runProvisioning for any message type (verified against the switch/case in ConversationChannelUsageDeploymentStatusService). So a Provisioning status at Stage 3 should not happen in steady state. Possible causes if it does: an exception thrown after the external call succeeded but before the PLSQL terminal-write ran; unusual instance config with async observer execution; or a future MessageType whose dispatch behavior we haven't accounted for. If Stage 3 returned Provisioning, loop:
for i in 1 2 3 4 5 6 7 8 9 10; do
sleep 3
STATUS=$(sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT DeploymentStatus FROM MessagingChannelUsage WHERE Id='{MCU_ID}'" --json \
| node -e 'console.log(JSON.parse(require("fs").readFileSync(0,"utf8")).result.records[0].DeploymentStatus)')
case "$STATUS" in
Provisioning) continue ;;
Active|Error) break ;;
esac
doneMax wait: 30s. If still Provisioning after the loop: emit {ok:false, kind:"timeout", mcuId, lastStatus:"Provisioning", elapsedS:30}. Verified on WhatsApp/wadtesting 2026-04-30: this loop should never actually iterate.
MessagingChannel.IsActiveThe observer flips IsActive inside the same callback as the status terminal write. In principle this is set by the time the PATCH returns. Confirm:
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, IsActive FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" --json > /tmp/amc-verify-channel.jsonIf IsActive === true: compute durationMs = Date.now() - T0 and emit success. If IsActive !== true despite MCU Active: the IsActive sync pass skipped (e.g. isTransitioningStatus was true when the observer ran, which shouldn't happen post-terminal). Emit:
{"ok": false, "kind": "provisioning-error", "mcuId": "...", "errorReason": "mcu-active-but-channel-inactive",
"errorDetails": "MCU DeploymentStatus=Active but MessagingChannel.IsActive=false — observer's IsActive sync pass didn't run. Inspect MessagingChannelUsageFunctionsHelper.handlePostSave."}Build the success envelope:
{"ok": true, "channelId": "{CHANNEL_ID}", "mcuId": "{MCU_ID}", "deploymentStatus": "Active",
"isActive": true, "messageType": "{MESSAGE_TYPE}", "durationMs": 20934}If this skill is the leaf (user invoked it directly), render:
Success — Activated — MessagingChannel {CHANNEL_ID} ({messageType}) is now live. MCU DeploymentStatus=Active, IsActive=true. (~{durationMs/1000}s)Info: Already active — MessagingChannel {CHANNEL_ID} is IsActive=true. No changes. (no-op path)Error: Routing not configured — run 'service-de-channel-routing-configure' skill first. (no-routing)Error: No MessagingChannelUsage row — run the insertion skill first. (no-mcu)Error: Readiness check failed: {message} — if it mentions consent/keywords, run 'service-de-channel-settings-configure' first. (readiness-failed)Error: Activation failed: {errorReason} — {errorDetails} (provisioning-error)Error: WhatsApp phone number verification failed: {hint} (verification-failed)Error: Could not request verification code: {message} (verification-request-failed)Timeout: Activation timed out after {elapsedS}s with MCU.DeploymentStatus={lastStatus}. Defensive-poll limit hit; this is unusual. Check MCU {mcuId} in Setup. (timeout)For end-to-end activation traces (WhatsApp happy-path, Apple activation, a readiness-validator failure, and the already-active no-op), see references/worked-examples.md.
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, OAuth token extraction, and the Status-code-409 Admin API conflict. Before troubleshooting an unexpected result or modifying this skill, load references/gotchas.md and follow it.
© forcedotcom, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 3 other files (references) in skills/service-de-channel-activate of forcedotcom/sf-skills.
Open the folder on GitHubat commit 4bbae5c
Service De Channel Activate next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Service De Channel Activate this skillforcedotcom/sf-skills | 1.1k | — | ~4.9k | Automated safety check: Pass | Apache-2.0 | |
| Security And Hardeningpenpot/penpot | 61k | 6 repos | ~4.7k | Automated safety check: Notes | MPL-2.0 | |
| Security Auditoreigent-ai/eigent | 15k | — | ~1.8k | Automated safety check: Notes | Apache-2.0 | |
| Security Reviewjewbetcha/opentrace | 116 | 18 repos | ~3.1k | Automated safety check: Notes | MIT | |
| Strix Code Vulnerability Scanusestrix/strix | 68k | — | ~1.1k | Automated safety check: Pass | Apache-2.0 | |
| Code Audit3stoneBrother/code-audit | 892 | 1 repos | ~2.7k | Automated safety check: Pass | None |
penpot/penpot
Hardens code against vulnerabilities. An agent skill from penpot/penpot.
eigent-ai/eigent
Audits source code, dependencies and config files for vulnerabilities and hardcoded secrets, using two bundled Python scanners and an OWASP Top 10 checklist.
jewbetcha/opentrace
A skill your agent uses when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features.
usestrix/strix
Runs a Strix white-box security review that reads the source, then exploits what it finds in a sandbox so each reported issue has a proof-of-concept.
3stoneBrother/code-audit
Professional code security audit skill covering 55+ vulnerability types.
usestrix/strix
Triages findings from a Strix pentest by severity, fixes each root cause with a minimal change, and re-runs Strix to confirm the exploit no longer works.
forcedotcom/sf-skills
Declared architecture snapshot for one Agentforce agent: planner, topics, actions, flows, Apex, prompt templates, and NGA plugins.
forcedotcom/sf-skills
Data Cloud 360° view of a single Agentforce session. An agent skill from forcedotcom/sf-skills.
forcedotcom/sf-skills
Apply a Salesforce sandbox post-copy automation JSON config against a target org.
forcedotcom/sf-skills
Apply a Salesforce sandbox post-copy automation JSON config against a target org.
forcedotcom/sf-skills
Apply SLDS-compliant UI using the correct blueprints, styling hooks, utility classes, and icons.
forcedotcom/sf-skills
Lightning Web Components with PICKLES methodology and 165-point scoring.
Works with
Categories
Activate an Enhanced MessagingChannel (WhatsApp/Apple/Facebook/SMS/RCS) by PATCHing MessagingChannelUsage.DeploymentStatus from Disabled to Provisioning via the REST sobject endpoint. Service De Channel Activate is an agent skill from forcedotcom/sf-skills.DeploymentStatus from Disabled to Provisioning via the REST sobject endpoint.
Service De Channel Activate fits situations like: activate an already-inserted Enhanced channel headlessly via REST; full end-to-end setup — use service-de-headless-channel-configure.
Run `npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a claude-code`. Or copy the skill folder (skills/service-de-channel-activate in forcedotcom/sf-skills) into .claude/skills/service-de-channel-activate in your project. Claude Code loads it when a task matches its description.
Run `npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a codex`. Or copy the skill folder (skills/service-de-channel-activate in forcedotcom/sf-skills) into .agents/skills/service-de-channel-activate in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add forcedotcom/sf-skills --skill service-de-channel-activate -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/service-de-channel-activate, .gemini/skills/service-de-channel-activate, .github/skills/service-de-channel-activate and .opencode/skills/service-de-channel-activate in your project.
Going by SKILL.md and its folder, Service De Channel Activate needs the command-line tools its instructions call (sf and node) and credentials named MESSAGING_PLATFORM_KEY. Our summary lists: A credential in MESSAGING_PLATFORM_KEY.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Service De Channel Activate is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.9k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 3.3k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Service De Channel Activate: Security And Hardening (penpot/penpot, 61k stars), Security Auditor (eigent-ai/eigent, 15k stars), Security Review (jewbetcha/opentrace, 116 stars) and Strix Code Vulnerability Scan (usestrix/strix, 68k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
forcedotcom (a GitHub organization) maintains it in forcedotcom/sf-skills, which has 1,067 GitHub stars. The repository holds 252 skills in this directory. The repository was last updated on October 9, 2026.
Source: forcedotcom/sf-skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.