service-de-channel-create について
Salesforce のチャネル設定フローから呼び出す内部スキルで、未アクティブの Enhanced MessagingChannel を作成します。WhatsApp、Line、AppleBusinessChat、Facebook、Text に対応し、Connect REST API と事前確認で冪等性を保ちます。
主な機能
- 015 種類のメッセージチャネルに対応
- 02Connect REST API によるチャネル作成
- 03プラットフォームキーと種別による既存レコード確認
- 04種別固有の前提条件への対応
- 05統一された結果形式の返却
活用シーン
- 01チャネルの一括設定フローでの挿入
- 02再開時の既存チャネル検出と再利用
- 03対応する種別の未アクティブチャネル作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill service-de-channel-createインストール後、Claude Code で/service-de-channel-createを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/apple.mdconnect-insert.mdfacebook.mdline.mdsms.mdwhatsapp.mdworked-examples.md詳細
メッセージングチャネルの挿入
参照ファイル一覧
| 参照ファイル | 読み込むタイミング |
|---|---|
references/whatsapp.md | MESSAGE_TYPE=WhatsApp の場合 — WABA のパートナーシップ(ステージ 2)と SF 側の WABA 管理(ステージ 3)の前提条件、リクエストボディ、WhatsApp 固有の注意点。 |
references/line.md | MESSAGE_TYPE=Line の場合 — オプションのトークン検証、authDetails のボディ形式、LINE 固有の注意点。 |
references/apple.md | MESSAGE_TYPE=AppleBusinessChat の場合 — 空の設定ボディ、Apple の前提条件に関するエラー、Apple 固有の注意点。 |
references/facebook.md | MESSAGE_TYPE=Facebook の場合 — ブラウザー OAuth → ページ選択のフロー、Facebook 固有の注意点。 |
references/sms.md | MESSAGE_TYPE=Text の場合 — 電話番号のプラットフォームキー、存在するものの検証されない smsProvider、電話番号のプロビジョニング要件(呼び出し元側)、SMS 固有の注意点。 |
references/connect-insert.md | 共通の authProviderId の検索、Connect POST、レスポンス分類の詳細(ステージ 4)。 |
references/worked-examples.md | タイプごとのエンドツーエンドのトレース(新規挿入、事前確認での早期終了、OAuth)。 |
このスキルの機能
{MESSAGE_TYPE} とその入力を受け取り、PlatformType=Enhanced、IsActive=false の MessagingChannel レコードを作成します。同じサーバー側のパス(LiveMessageSetupServiceImpl.addChannel → LiveMessageChannelsUtil.findOrCreateCsotMessagingChannel)では、MessagingChannelUsage 行も挿入されます(新規挿入時は DeploymentType=Conversation、DeploymentStatus=New — 以前に有効化された後に無効化されたチャネルでは Disabled)。こちらは自動的に作成されます。
すべてのタイプに対する主要なパスは 1 つです:Connect REST API。
sf api request rest で管理される組織の OAuth セッションを使い、POST /services/data/v{V}/connect/livemessage/channels を実行します。公開され、正式にサポートされており、API v66 以降で利用できます。Aura セッションも、シェルの状態へのトークン抽出もありません。
(MessagingPlatformKey, MessageType) を基準に冪等です。同じプラットフォームキーで再実行すると、既存チャネルの id が返されます。MessagingChannel レコードは通常の方法では削除できないため、事前確認(ステージ 1)が実質的な冪等性のガードとして重要です。
すべてのタイプは同じエンベロープ形式を返すため、挿入後に呼び出し元がタイプごとに処理を分ける必要はありません。
{ok, channelId, mcuId, developerName, isActive:false, messageType, messagingPlatformKey, path, created, durationMs}。
対応タイプ(分岐点はここだけ)
{MESSAGE_TYPE} | プラットフォームキー(messagingPlatformKey) | 前提条件(呼び出し元から提供) | ボディの追加項目 | 参照 |
|---|---|---|---|---|
WhatsApp | Phone Number ID | service-de-waba-integrate(ステージ 2~3 で実行) | externalAccountId、isoCountryCode | whatsapp.md |
Line | LINE channel id | LINE Developers Console の LINE id、secret、token | authDetails.{client_secret,access_token} | line.md |
AppleBusinessChat | Apple BC Account ID(GUID) | Apple BC Account ID(register.apple.com/business-chat) | (なし — 空の設定) | apple.md |
Facebook | Facebook Page ID | OAuth は内部で処理 | authDetails: {}(OAuth から取得) | facebook.md |
Text | 電話番号 / ショートコード | 電話番号のプロビジョニング済み(呼び出し元から提供) | smsProvider(キーは必須、値は "" でも可)、isoCountryCode | sms.md |
非対応(個別のロジックが必要ですが、まだ実装されていません):WeChat、MsCopilot、Alexa。
これらの場合は、unsupported-type エンベロープを出力します(ステージ 0)。
このスキルを使用しない場合
- エンドツーエンドのセットアップ。 ユーザーが挿入 → ルーティング → 有効化を希望する場合は、
service-de-headless-channel-configureを呼び出します。このスキルに加え、ルーティング、同意、有効化をオーケストレーションします。このスキルが扱うのは挿入の手順だけです(ステージ 0 を参照)。 - チャネルがすでに存在し、再ルーティングまたは有効化だけを行いたい場合。 挿入は省略し、既存の
channelIdを使ってservice-de-channel-routing-configure/service-de-channel-activateを呼び出します。
入力(呼び出し元から)
{MESSAGE_TYPE}—WhatsApp、Line、AppleBusinessChat、Facebook、Textのいずれかです。 大文字と小文字を区別します(SF の選択リストと一致)。SMS の列挙値はTextです —SMSタイプはありません。{ORG_ALIAS}— 任意です。sfCLI のターゲット組織エイリアスです。デフォルト:sf config get target-org。{API_VERSION}— 任意です。Connect API バージョンです。デフォルト:組織の現在の API バージョン (sf org display --json | jq -r '.result.apiVersion')。404 を避けるため、必ず組織固有のバージョンを使用してください。{CHANNEL_NAME}— Setup → Messaging Settings に表示される名前です。AppleBusinessChatでは必須です(電話番号/画面名による代替はありません)。ほかのタイプでは省略された場合に入力を求めます。
タイプ固有の入力:
- WhatsApp:
{WABA_ID}(externalAccountIdとして送信)、{PHONE_NUMBER_ID}(プラットフォームキー — WABA_ID とは異なる必要があります)、{ISO_COUNTRY_CODE}(任意、デフォルトはUS)。 - Line:
{LINE_CHANNEL_ID}(プラットフォームキー)、{LINE_CHANNEL_SECRET}、{LINE_ACCESS_TOKEN}、{VERIFY_TOKEN_FIRST}(任意、デフォルトはtrue)。 - AppleBusinessChat:
{APPLE_BC_ID}(プラットフォームキー、GUID)。 - Facebook:
{PAGE_ID}(任意 — 指定がない場合は OAuth で取得するか、入力を求めます)。 - Text(SMS):
{SMS_NUMBER}(プラットフォームキー — 電話番号/ショートコードの文字列。組織へのプロビジョニングがすでに完了している必要があります)、{SMS_PROVIDER}(smsProviderとして送信 — リクエスト本文にsmsProviderキーが含まれている必要があります。含まれていない場合、挿入は「SMS provider is missing」というエラーで 400 を返します。ただし、値は検証も保存もされないため、空文字列""でも機能します。呼び出し元からプロバイダーが指定されない場合は""をデフォルト値とします)、{ISO_COUNTRY_CODE}(任意、デフォルトはUS)。このスキルは電話番号をプロビジョニングしません — 呼び出し元は使用する番号をあらかじめ把握している必要があります。
出力(呼び出し元へ)
成功:
{"ok": true, "channelId": "0Mj...", "mcuId": "0gL...", "developerName": "...",
"isActive": false, "messageType": "{MESSAGE_TYPE}", "messagingPlatformKey": "...",
"path": "connect" | "preflight", "created": true | false, "durationMs": 1234}
created: false と path: "preflight" は、行がすでに存在し、冪等に返されたことを意味します。
失敗(共通):
{"ok": false, "kind": "wrong-skill", "correctSkill": "service-de-headless-channel-configure", "hint": "..."}
{"ok": false, "kind": "unsupported-type", "supportedTypes": ["WhatsApp","Line","AppleBusinessChat","Facebook","Text"], "hint": "..."}
{"ok": false, "kind": "missing-input", "missing": ["..."], "hint": "caller must provide all required inputs for this type"}
{"ok": false, "kind": "auth", "hint": "OAuth token invalid / expired — run 'sf org login web'"}
{"ok": false, "kind": "business", "message": "...", "errorCode": "..."}
{"ok": false, "kind": "transport","status": 500, "message": "..."}
{"ok": false, "kind": "verify-failed", "hint": "Connect returned 201 but follow-up SOQL shows no matching channel"}
タイプ固有の失敗の種類(WhatsApp の partnership-blocked、user-declined、waba-manage-failed、
meta-precondition、LINE の line-token-invalid、Apple の apple-precondition、Facebook の oauth-not-complete、oauth-failed)は、タイプごとのリファレンスファイルに記載されています。
ステージ 0:オーケストレーター経由の呼び出しのみを許可する
このスキルは内部使用専用です。 service-de-headless-channel-configure オーケストレーターからのみ呼び出してください。
ユーザーの元のリクエストが、メッセージングチャネルの「インストール」、「セットアップ」、「作成」、「有効化」(またはエンドツーエンドのセットアップを意味する表現)である場合は、ただちに次を返します。
{
"ok": false,
"kind": "wrong-skill",
"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.",
"correctSkill": "service-de-headless-channel-configure"
}
そして、ユーザーに次を表示します:
Error: Wrong skill invoked.
This skill (service-de-channel-create) only handles the insertion step.
For complete channel setup, use: /service-de-headless-channel-configure
That orchestrator runs: insert → configure routing → activate in one flow.
次の場合にのみステージ 0.1 に進んでください:
- このスキルが
service-de-headless-channel-configureによってプログラムから呼び出された - ユーザーが挿入のみを希望すると明示している(ルーティングや有効化は希望していない)
- ルーティング/有効化がすでに成功している状態で、再試行/再開するケースである
判断に迷った場合は、オーケストレーターに誘導してください。
ステージ 0.1:タイプと必須入力を検証する
サポート対象タイプの表で {MESSAGE_TYPE} を確認します。該当しない場合は、unsupported-type エンベロープを出力して終了します。
タイプに必要な入力がそろっていることを検証します(Facebook は例外 — 必須入力はありません。Facebook のフロー内で OAuth → ページ選択 → 入力プロンプトが行われます)。不足があれば、不足している項目のリストとともに missing-input を出力して終了します。
WhatsApp のみ: さらに {WABA_ID} != {PHONE_NUMBER_ID} を確認します — これらは異なる Meta エンティティであり、両方に同じ値を使うと原因が分かりにくい有効化エラーが発生します。同じ場合は、{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."} を出力します。
durationMs 用に {T0} = Date.now() を記録します。
ステージ 0.2:スクラッチディレクトリを設定する
このスキルが書き込む一時的なアーティファクト(事前確認クエリの結果、リクエストボディ、Connect のレスポンス)はすべて、ハーネスの作業領域内にルートを置く、実行ごとのスクラッチディレクトリに保存します。誰でも書き込み可能で移植性のない、単独の絶対パス /tmp は決して使用しないでください。一度だけ設定します。
SCRATCH_DIR="$(mktemp -d "${outputDir:-${TMPDIR:-/tmp}}/icc.XXXXXX")"
基本ディレクトリには、設定されている場合はハーネスから提供される ${outputDir} を、そうでなければプライベートなユーザー専用の ${TMPDIR} を使用し、最後の手段としてのみ /tmp を使用します。以降のすべてのステージ(および参照フロー)では ${SCRATCH_DIR}/ 以下に書き込みます。永続レポートは ${outputDir} 以下に保存します。
ステージ 1:事前確認 — チャネルはすでに存在しますか?
まず既存のレコードを SOQL で照会します。見つかった場合は、すべての前提条件と挿入を省略できます。
{PAGE_ID} のない Facebook の場合、このステージを省略します — ページ ID は OAuth → ページ選択が完了するまでわかりません(references/facebook.md を参照)。ページ選択後に事前確認を実行してください。
サブクエリではなく、2 つのクエリを使用します。 (SELECT ... FROM MessagingChannelUsages) の子サブクエリは、子リレーションシップの名前を特定できない組織で INVALID_TYPE エラーになります(wadtesting / PRODDEBUG で確認済み)。外部キーを使った形式は、どこでも機能します。
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeveloperName, MasterLabel, IsActive, MessagingPlatformKey FROM MessagingChannel WHERE MessagingPlatformKey = '{PLATFORM_KEY}' AND MessageType = '{MESSAGE_TYPE}'" \
--json > "${SCRATCH_DIR}/preflight.json"
records.length === 1 の場合:
{CHANNEL_ID}、{DEVELOPER_NAME}、{IS_ACTIVE}を記録します。- MCU を外部キーで取得するための 2 つ目のクエリ:
sf data query --target-org '{ORG_ALIAS}' \ --query "SELECT Id, DeploymentStatus FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" \ --json > "${SCRATCH_DIR}/preflight-mcu.json"{MCU_ID}を記録します(MCU が空の場合はフラグを立てますが、失敗にはしません。有効化スキルが処理します)。 path: "preflight", created: falseを含む成功エンベロープを出力します。ここで終了します。
records.length > 1 の場合:曖昧です(本来、一意性によって防止されるはずです)。最初のレコードで続行し、エンベロープに警告を記録します。
records.length === 0 の場合:ステージ 2 に進みます。
ステージ 2~3:タイプ別の前提条件
ほとんどのタイプでは、ここでの前提条件はありません。ステージ 4 に進んでください。
- WhatsApp: Meta WABA と Salesforce のパートナーシップ(ステージ 2。
service-de-waba-integrate経由)、および Salesforce 側の Webhook サブスクリプションとクレジットラインの共有(ステージ 3、POST /connect/livemessage/whatsapp/business-account)が必要です。挿入する前にreferences/whatsapp.mdを読み込み、その手順に従ってください。 - Line:
{VERIFY_TOKEN_FIRST}が true の場合、https://api.line.me/v2/bot/infoに対するトークンの事前確認は任意です。references/line.mdを参照してください。 - Facebook: ブラウザーを使った OAuth 認証、ページリストの取得、ページ選択を、この段階で挿入前に行います。
references/facebook.mdを読み込み、その手順に従ってください。 - Apple: なし。(Apple の証明書ハンドシェイクは Apple 側でプロビジョニングされ、チャネル挿入時ではなく、実行時にメッセージごとに行われます。
references/apple.mdを参照してください。) - Text(SMS): 挿入時にはありません。電話番号はすでに組織にプロビジョニングされている必要があり、呼び出し元はその
smsProviderを把握している必要があります。ただし、このスキルではプロビジョニングを行いません。呼び出し元がすでに所有する電話番号のレコードを挿入します。(ESP 登録は挿入時ではなく、有効化時に実行されます。references/sms.mdを参照してください。)
ステージ 4:Connect REST API 経由で挿入する
すべてのタイプに共通です。タイプごとに異なるのはリクエストボディだけです。authProviderId の検索、POST の実行、レスポンス分類テーブルはすべて共通です。
Stage 4.1: authProviderId(組織全体;Text 以外のすべてのタイプで必須)
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id FROM AuthProvider WHERE DeveloperName = 'LiveMessageSetup'" \
--json > "${SCRATCH_DIR}/authprovider.json"
1 行 → {AUTH_PROVIDER_ID}。0 行 → タイプに応じて分岐します:
Text— 致命的ではありません。Text の Connect シグネチャはauthProviderIdを受け取らないため、{AUTH_PROVIDER_ID}は未設定のまま Stage 4.2 に進みます。POST ではパラメーターを完全に省略します( 以下を参照)。Text ではビジネスエラーを出力しないでください — LiveMessage のセットアップを一度も実行したことがない組織こそ、 Text のみのパスが重要になる場面であり、ここでブロックすると、告知されている SMS の挿入がその組織では 到達不能になります。- その他すべてのタイプ(
WhatsApp、Line、AppleBusinessChat、Facebook)— 組織で LiveMessage のセットアップが一度も実行されておらず、 挿入を続行できません。次を出力します:{ok:false, kind:"business", message:"No AuthProvider 'LiveMessageSetup' found — run the Messaging Setup wizard once on this org first"}。
この URL パラメーターがない場合、Connect エンドポイントは POST を 400 ILLEGAL_QUERY_PARAMETER_VALUE "Missing argument authProviderId" で拒否します — Text を除くすべてのタイプで確認済みです。Apple も含みます。(Text には、これを必要としない独自の Connect シグネチャがあります。)検索で行が見つかった場合は、Text を含むすべてのタイプで authProviderId を送信します(Text では無害です)。見つからない場合は、Text のみ続行できます。その場合はパラメーターを付けずに続行します。authProviderId は URL クエリ文字列に指定し、本文には含めません(本文に含めると → 400 JSON_PARSER_ERROR "Unrecognized field 'authProviderId'")。
Stage 4.2: タイプ固有の本文を作成して POST する
まず {CHANNEL_NAME} を解決します(必須で未指定の場合はプロンプトを表示 — タイプごとのリファレンスを参照)。次に、対応タイプの表に基づいて本文を作成し、POST します。本文はファイルに書き込みます(--body @...)— LINE のトークンは長く、シェルで扱いにくい文字が含まれる場合があります。
authProviderId が Stage 4.1 で解決された場合にのみ、そのクエリパラメーターを追加します — それ以外(AuthProvider がない Text の場合)は、空の ?authProviderId= でも同じ ILLEGAL_QUERY_PARAMETER_VALUE が発生するため、ベースのエンドポイントに POST します:
URL="/services/data/v{API_VERSION}/connect/livemessage/channels"
[ -n "${AUTH_PROVIDER_ID:-}" ] && URL="${URL}?authProviderId=${AUTH_PROVIDER_ID}"
sf api request rest "${URL}" \
--method POST --target-org '{ORG_ALIAS}' \
--header 'Content-Type: application/json' --header 'Accept: application/json' \
--body @"${SCRATCH_DIR}/body.json" --include > "${SCRATCH_DIR}/connect-response.txt" 2>&1
--include は本文の前に HTTP ステータス/ヘッダーブロックを出力します — ステータスは末尾の -w マーカーではなく、そこから読み取ります。sf api request rest は OAuth を内部で処理します。
タイプごとの正確な本文形式と、完全なレスポンス分類表(Stage 1 に戻る DUPLICATE_VALUE の競合、Facebook の AUTH_PROVIDER_NEEDS_AUTH における 401 と 400 の判別、タイプごとの前提条件エラーを含む)については、references/connect-insert.md とタイプごとのリファレンスを読み込んでください。
Stage 5: 挿入を検証する
2 つのクエリを使用します(Stage 1 と同じく、サブクエリを使用できないため)。すべての組織で使える共通フィールドセットを使用します —
ExternalAccountId と MessagingPlatform は、すべての組織に存在するわけではありません。
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeveloperName, MasterLabel, MessageType, IsActive, MessagingPlatformKey FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" --json
sf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeploymentStatus, DeploymentType FROM MessagingChannelUsage WHERE MessagingChannelId = '{CHANNEL_ID}'" --json
想定結果:チャネルの IsActive=false;MCU がちょうど 1 件あり、DeploymentType=Conversation、
DeploymentStatus が {New, Disabled} のいずれかであること(新規挿入では New になります;Disabled は
以前に有効化された後、無効化されたチャネルです — どちらも有効化を開始できる状態です)。
MCU クエリが空の場合 → {ok:false, kind:"verify-failed", hint:"Connect returned {CHANNEL_ID} but no child MessagingChannelUsage was created"}。
MasterLabel/DeveloperName については、Connect のレスポンス本文ではなく DB を信頼してください — 201 の本文には
古い入力レプリカの値が含まれています(タイプごとの注意事項を参照)。
ステージ 6:呼び出し元に報告
{"ok": true, "channelId": "{CHANNEL_ID}", "mcuId": "<from Stage 5>",
"developerName": "<authoritative, from Stage 5>", "isActive": false,
"messageType": "{MESSAGE_TYPE}", "messagingPlatformKey": "{PLATFORM_KEY}",
"path": "connect", "created": true, "durationMs": <Date.now() - T0>}
直接呼び出された場合(オーケストレーター経由ではない場合)の出力:
Success — MessagingChannel {CHANNEL_ID} ({developerName}) created via {path}. MCU {mcuId} auto-created. IsActive=false — run routing, then consent, then activate next.Info: Channel already exists — {CHANNEL_ID} ({developerName}). Proceed to routing/activation.Error: {kind}: {message or hint}
成功した場合、呼び出し元は返された channelId を使って、すぐに
service-de-channel-routing-configure、続いて service-de-channel-settings-configure、その後
service-de-channel-activate を呼び出せます。この 3 つはメッセージタイプに依存せず(
MessagingChannel.Id と MessagingChannelUsage.Id を操作するため)、後続の振り分けに関する懸念はありません。
注意点(共通)
- タイプ名は大文字と小文字を区別します。
"whatsapp"≠"WhatsApp"— Salesforce の選択リスト、SOQL フィルター、 Connect の列挙型はいずれも完全一致します。上流で正規化してください。ステージ 0.1 を大文字と小文字を区別しない仕様にしないでください。 - 事前チェックが冪等性のガードです。
MessagingChannelの行は標準的な方法では削除できないため、再実行時はステージ 1 で既存の行を検出する必要があります。 - エンベロープの形式は呼び出し元と密接に結び付いています。 ここでフィールド名を変更すると、すべての呼び出し元
(特に
service-de-headless-channel-configure)に変更が通知されないまま反映されます。フィールド名を読み替えるのではなく、テストで明示的に失敗させてください。 - タイプごとの注意点はリファレンスファイルに記載されています。 WABA と電話番号 ID の混同、LINE の長期トークンと短期トークン、Apple の空の設定ボディとプロビジョニング呼び出しがない点、Facebook の OAuth ページ選択と 401 エラーの判別など、そのタイプの問題をデバッグする際は、該当する
references/<type>.mdを読み込んでください。