Loading...
Loading...
Compare original and translation side by side
{CHANNEL_ID}MessagingChannelUsage.IdPATCH /services/data/v{V}/sobjects/MessagingChannelUsage/{MCU_ID}{"DeploymentStatus":"Provisioning"}MessagingChannelUsageFunctions.validateBeforeSavevalidateDeploymentStatusvalidateChannelReadinessOnProvisioningFIELD_INTEGRITY_EXCEPTIONMessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnceMessagingChannelUsageFunctionsHelper.handlePostSaveTransactionObserverConversationChannelUsageDeploymentStatusService.handleProvisioningAbstractChannelUsageDeploymentStatusServicerunProvisioningswitchMessageTypeWHATS_APPregisterCsotWhatsAppNumberLiveMessageSetupApi.registerWhatsAppNumber/registerFACEBOOKmetaGraphApiService.subscribeFacebookPageTEXTregisterCsotSmsAppleBusinessChatLinedefaultDeploymentStatus = 'Active'DeploymentStatus = 'Error'ErrorReasonErrorDetailsMessagingChannel.IsActive = trueActiveisTransitioningStatusProvisioning/register{CHANNEL_ID}MessagingChannelUsage.IdPATCH /services/data/v{V}/sobjects/MessagingChannelUsage/{MCU_ID}{"DeploymentStatus":"Provisioning"}MessagingChannelUsageFunctions.validateBeforeSavevalidateDeploymentStatusvalidateChannelReadinessOnProvisioningFIELD_INTEGRITY_EXCEPTIONMessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnceMessagingChannelUsageFunctionsHelper.handlePostSaveTransactionObserverConversationChannelUsageDeploymentStatusService.handleProvisioningAbstractChannelUsageDeploymentStatusServicerunProvisioningMessageTypeWHATS_APPregisterCsotWhatsAppNumberLiveMessageSetupApi.registerWhatsAppNumber/registerFACEBOOKmetaGraphApiService.subscribeFacebookPageTEXTregisterCsotSmsAppleBusinessChatLinedefaultDeploymentStatusActiveDeploymentStatusErrorErrorReasonErrorDetailsActiveMessagingChannel.IsActive = trueisTransitioningStatusProvisioning/register| Reference file | Load when |
|---|---|
| Stage 3 comes back with |
| You want a reference run of the WhatsApp happy-path, Apple activation, a readiness failure, or the already-active no-op. |
| Troubleshooting an unexpected result, or before modifying this skill — the eleven known gotchas. |
| 参考文件 | 加载时机 |
|---|---|
| 阶段3返回 |
| 需要参考WhatsApp正常流程、Apple激活流程、就绪检查失败流程或已激活无操作流程的运行示例时。 |
| 排查异常结果或修改本技能前——查看已知的11个注意事项。 |
activateChannelUsagereturnValueMessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnceMessagingChannelUsage.entity.xmlDeploymentStatuseditAccess="always"<readonly>getValidAPIStatusTransitionsDisabled → ProvisioningNew → ProvisioningError → Provisioning | DeprovisioningActive → DeprovisioningProvisioning → Active | ErrorProvisioningactivateChannelUsagereturnValueMessagingChannelUsageFunctions.saveHook_PostStmtExecuteOnceMessagingChannelUsage.entity.xmlDeploymentStatuseditAccess="always"<readonly>getValidAPIStatusTransitionsDisabled → ProvisioningNew → ProvisioningError → Provisioning | DeprovisioningActive → DeprovisioningProvisioning → Active | ErrorProvisioningIsActive=trueActive → ProvisioninggetValidAPIStatusTransitions()noop:trueactivateChannelUsageLiveMessageSetupExceptionnullQueueIdvalidateChannelReadinessOnProvisioningSessionHandlerId=null && FallbackQueueId=nullFIELD_INTEGRITY_EXCEPTIONservice-de-channel-routing-configureaddChannelIsActive=trueActive → ProvisioninggetValidAPIStatusTransitions()noop:trueactivateChannelUsageLiveMessageSetupExceptionnullQueueIdvalidateChannelReadinessOnProvisioningSessionHandlerId=null && FallbackQueueId=nullFIELD_INTEGRITY_EXCEPTIONservice-de-channel-routing-configureaddChannel{CHANNEL_ID}MessagingChannel.Id0MjSessionHandlerIdFallbackQueueId{ORG_ALIAS}sfsf config get target-org{API_VERSION}68.0MessagingChannelUsage{POLL_TIMEOUT_S}{POLL_INTERVAL_S}{CHANNEL_ID}MessagingChannel.Id0MjSessionHandlerIdFallbackQueueId{ORG_ALIAS}sfsf config get target-org{API_VERSION}68.0MessagingChannelUsage{POLL_TIMEOUT_S}{POLL_INTERVAL_S}{"ok": true, "channelId": "0Mj...", "mcuId": "0gL...", "deploymentStatus": "Active", "isActive": true, "messageType": "WhatsApp", "durationMs": 20934}{"ok": true, "noop": true, "channelId": "0Mj...", "message": "Channel already active"}{"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"}{"ok": false, "kind": "readiness-failed", "errorCode": "FIELD_INTEGRITY_EXCEPTION",
"message": "...", "hint": "validateChannelReadinessOnProvisioning rejected the write — most commonly missing consent; run service-de-channel-consent-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"}{"ok": false, "kind": "auth", "hint": "OAuth token invalid / expired — run 'sf org login web'"}
{"ok": false, "kind": "transport", "status": 500, "message": "..."}{"ok": true, "channelId": "0Mj...", "mcuId": "0gL...", "deploymentStatus": "Active", "isActive": true, "messageType": "WhatsApp", "durationMs": 20934}{"ok": true, "noop": true, "channelId": "0Mj...", "message": "Channel already active"}{"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"}{"ok": false, "kind": "readiness-failed", "errorCode": "FIELD_INTEGRITY_EXCEPTION",
"message": "...", "hint": "validateChannelReadinessOnProvisioning rejected the write — most commonly missing consent; run service-de-channel-consent-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"}{"ok": false, "kind": "auth", "hint": "OAuth token invalid / expired — run 'sf org login web'"}
{"ok": false, "kind": "transport", "status": 500, "message": "..."}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.jsonchannel = /tmp/amc-precheck-channel.json records[0]mcu = /tmp/amc-precheck-mcu.json records[0]| Condition | Envelope |
|---|---|
| Channel query returned 0 records | |
| |
| |
| MCU query returned 0 records | |
| Otherwise | Record |
{T0}durationMssf 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.jsonchannel = /tmp/amc-precheck-channel.json records[0]mcu = /tmp/amc-precheck-mcu.json records[0]| 条件 | 返回结果 |
|---|---|
| 通道查询返回0条记录 | |
| |
| |
| MCU查询返回0条记录 | |
| 其他情况 | 记录 |
{T0}durationMs{INITIAL_MCU_STATUS} === "Provisioning"ActiveErrorProvisioningProvisioning{INITIAL_MCU_STATUS} === "Provisioning"ActiveErrorProvisioningProvisioningMessagingChannelUsage.DeploymentStatus = "Provisioning"MessagingChannelUsage.DeploymentStatus = "Provisioning"sf api request rest/registersf api request restsf 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-w| Status | Body | Handling |
|---|---|---|
| 204 | empty | Success. The observer ran to completion; MCU is |
| 400 | | Validator rejection. See table below. |
| 400 | | Skill bug — the MCU_ID from Stage 1 was wrong, or the body shape is off. Emit |
| 401 | (usually empty) | Bearer token invalid. Emit |
| 403 | | User lacks perm to write |
| 5xx | varies | Transport. The observer may have partially committed — Stage 3's SOQL is the source of truth. Read MCU state; if it's |
FIELD_INTEGRITY_EXCEPTION| Message fragment | Meaning | Envelope |
|---|---|---|
| "invalid deployment status transition" | The current status doesn't allow | Re-read MCU; if now |
| "consent" / "keyword" / mentions of STOP/HELP | | |
| "routing" / "queue" / "SessionHandler" | Routing precondition (Stage 1 should have caught, but the validator re-checks). | |
| other | Unrecognized validator error. | |
sf api request rest/registersf api request restsf 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-w| 状态码 | 响应体 | 处理方式 |
|---|---|---|
| 204 | 空 | 成功。观察者已执行完毕;MCU状态为 |
| 400 | | 验证器拒绝请求。参见下方表格。 |
| 400 | | 技能存在bug——阶段1获取的MCU_ID错误,或请求体格式有误。返回 |
| 401 | 通常为空 | Bearer令牌无效。返回 |
| 403 | | 用户无写入 |
| 5xx | 内容不定 | 传输错误。观察者可能已部分提交——阶段3的SOQL查询是权威数据源。读取MCU状态;如果状态为 |
FIELD_INTEGRITY_EXCEPTION| 消息片段 | 含义 | 返回结果 |
|---|---|---|
| "invalid deployment status transition" | 当前状态不允许转换为 | 重新读取MCU;如果状态变为 |
| "consent" / "keyword" / 提及STOP/HELP | | |
| "routing" / "queue" / "SessionHandler" | 路由前置条件不满足(阶段1应已捕获,但验证器会重新检查)。 | |
| 其他 | 未识别的验证器错误。 | |
ActiveErrorsf 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 |
|---|---|
| Continue to Stage 4. |
| For WhatsApp channels only: phone number needs OTP verification. Continue to Stage 3.2 (WhatsApp verification flow). |
| Emit |
| Unexpected — the observer was supposed to terminate before the PATCH returned 204. Fall through to a defensive poll (see Stage 3.1). |
| The PATCH returned 204 but the write didn't take? Emit |
ActiveErrorsf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, DeploymentStatus, ErrorReason, ErrorDetails FROM MessagingChannelUsage WHERE Id = '{MCU_ID}'" \
--json > /tmp/amc-poststate-mcu.json| 状态 | 处理方式 |
|---|---|
| 继续执行阶段4。 |
| 仅适用于WhatsApp通道:手机号需要OTP验证。继续执行阶段3.2(WhatsApp验证流程)。 |
| 返回 |
| 异常情况——观察者应在PATCH返回204之前完成状态转换。执行防御性轮询(参见阶段3.1)。 |
| PATCH返回204但写入未生效?返回 |
ErrorReason === "VERIFICATION_REQUIRED"ErrorReason === "VERIFICATION_REQUIRED"references/phone-verification.mdErrorReason === "VERIFICATION_REQUIRED"ErrorReason === "VERIFICATION_REQUIRED"references/phone-verification.mdProvisioningProvisioningrunProvisioningConversationChannelUsageDeploymentStatusServiceProvisioningProvisioningfor 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
doneProvisioning{ok:false, kind:"timeout", mcuId, lastStatus:"Provisioning", elapsedS:30}runProvisioningConversationChannelUsageDeploymentStatusServiceProvisioningProvisioningfor 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
doneProvisioning{ok:false, kind:"timeout", mcuId, lastStatus:"Provisioning", elapsedS:30}MessagingChannel.IsActiveMessagingChannel.IsActiveIsActivesf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, IsActive FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" --json > /tmp/amc-verify-channel.jsonIsActive === truedurationMs = Date.now() - T0IsActive !== trueActiveIsActiveisTransitioningStatus{"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."}IsActivesf data query --target-org '{ORG_ALIAS}' \
--query "SELECT Id, IsActive FROM MessagingChannel WHERE Id = '{CHANNEL_ID}'" --json > /tmp/amc-verify-channel.jsonIsActive === truedurationMs = Date.now() - T0ActiveIsActive !== trueIsActiveisTransitioningStatus{"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."}{"ok": true, "channelId": "{CHANNEL_ID}", "mcuId": "{MCU_ID}", "deploymentStatus": "Active",
"isActive": true, "messageType": "{MESSAGE_TYPE}", "durationMs": 20934}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.Error: Routing not configured — run 'service-de-channel-routing-configure' skill first.Error: No MessagingChannelUsage row — run the insertion skill first.Error: Readiness check failed: {message} — if it mentions consent/keywords, run 'service-de-channel-consent-configure' first.Error: Activation failed: {errorReason} — {errorDetails}Error: WhatsApp phone number verification failed: {hint}Error: Could not request verification code: {message}Timeout: Activation timed out after {elapsedS}s with MCU.DeploymentStatus={lastStatus}. Defensive-poll limit hit; this is unusual. Check MCU {mcuId} in Setup.{"ok": true, "channelId": "{CHANNEL_ID}", "mcuId": "{MCU_ID}", "deploymentStatus": "Active",
"isActive": true, "messageType": "{MESSAGE_TYPE}", "durationMs": 20934}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.Error: Routing not configured — run 'service-de-channel-routing-configure' skill first.Error: No MessagingChannelUsage row — run the insertion skill first.Error: Readiness check failed: {message} — if it mentions consent/keywords, run 'service-de-channel-consent-configure' first.Error: Activation failed: {errorReason} — {errorDetails}Error: WhatsApp phone number verification failed: {hint}Error: Could not request verification code: {message}Timeout: Activation timed out after {elapsedS}s with MCU.DeploymentStatus={lastStatus}. Defensive-poll limit hit; this is unusual. Check MCU {mcuId} in Setup.references/worked-examples.mdreferences/worked-examples.mdIsActiveDeploymentStatusErrorReasonVERIFICATION_REQUIREDreferences/gotchas.mdIsActiveDeploymentStatusErrorReasonVERIFICATION_REQUIREDreferences/gotchas.md