Configure Messaging for In-App and Web (MIAW) channels in Service Cloud: Messaging Channel, Embedded Service Deployment, CORS/CSP Trusted Sites, pre-chat fields, session routing by Queue or Omni-Channel Flow, Status-Based capacity, Live Agent migration. Trigger keywords: MessagingChannel, EmbeddedServiceConfig, messagingChannelType, EmbeddedMessaging, sessionHandlerQueue, sessionHandlerFlow, embeddedConfig, automatedResponses, CorsWhitelistOrigin, CspTrustedSite, BrandingSet, MessagingSession, MessagingEndUser, enhanced chat, embedded messaging, chat widget, pre-chat form, auto-response. NOT for an Agentforce agent in the chat window - use agentforce/agent-channel-deployment. NOT for Omni-Channel enablement, Service Channels or skills-based routing - use admin/omni-channel-routing-setup.
This skill activates when a practitioner needs to set up or troubleshoot real-time or asynchronous chat channels in Salesforce Service Cloud using Messaging for In-App and Web (MIAW). It covers the full administrative configuration path from channel creation through Omni-Channel routing to agent capacity management.
Before Starting
Gather this context before working on anything in this domain:
Confirm the org has Messaging for In-App and Web enabled (Setup > Messaging Settings). This is separate from legacy Live Agent. The MessagingChannel metadata type is documented as available only when the org has the "Configure Messaging" and "View Setup and Configuration" permissions enabled for Messaging — a retrieve or deploy that fails on access rather than shape points here. There is no MessagingSettings metadata type to deploy the enablement itself.
Identify every domain (hostname + protocol) where the chat widget will be embedded. Each domain needs both a CORS Trusted Site and a CSP Trusted Site entry — missing either causes the widget to silently fail.
Determine whether routing will use a simple Queue assignment or a dynamic Omni-Channel Flow. Queue-based routing is simpler but Flow-based routing supports conditional logic such as language routing or business-hours fallback.
Decide the capacity model upfront: Tab-Based (capacity released when the console work tab closes) or Status-Based (capacity held until the work is completed or reassigned — the right model for asynchronous messaging, which outlives a tab). Since API 65.0 this is two settings that must agree: the org gate OmniChannelSettings.enableOmniStatusCapModel and the per-channel ServiceChannel.capacityModel. Both, and the capacity numbers on Presence Configurations, belong to admin/omni-channel-routing-setup.
Questions to Ask Before Configuring
Ask these before opening Setup. Each one maps to a behaviour in references/gotchas.md; an agent that skips them produces a channel that deploys cleanly and serves nobody.
Ask
Why it matters
What a good answer adds
"Which channel are we actually building — web/in-app chat, SMS, WhatsApp, or a partner channel?"
messagingChannelType lists WhatsApp, Facebook, Text and more, but the guide says third-party channels don't use this metadata type (gotchas #6)
The right value (EmbeddedMessaging or Custom) — or the finding that this skill is the wrong path
"Which Experience site or website does the widget live on, and does it exist yet?"
EmbeddedServiceConfig.site is Required; there is no site-less web deployment
The site's developer name and its place ahead of this work in the deploy order
"Which queue owns a session that the routing flow can't place, and who is in it?"
sessionHandlerQueue is Required and doubles as the flow's fallback; the failure is a fallback nobody can accept from (gotchas #5)
A named queue with a routing configuration and at least one available agent
"List every origin the widget loads from — production, staging, UAT, preview — with scheme and port."
A non-allowlisted origin gets HTTP 404, not a CORS error, so the symptom lies about the cause (gotchas #1)
The exact CORS and CSP entry list, and whether one wildcard pattern covers them
"Which resources does the host page need to load — scripts, frames, images, fonts, styles?"
Every CSP directive defaults to false, and a directive-free trusted site grants nothing (gotchas #8)
The isApplicableTo*Src set, chosen rather than defaulted
"Who links the conversation to a Contact, and on which record?"
MessagingSession.EndUserContactId is read-only; the writeable field is on MessagingEndUser (gotchas #3)
An owner and a target object for the identity automation, before agents see anonymous chats
"What does the customer hear when we're closed, when they go quiet, and when the agent leaves?"
Each is a separate automatedResponses type; responseTimeoutInMins is bounded at 5–60
The auto-response set and languages, instead of silence on three real paths
What a proper configuration adds over just creating a channel: the widget loads on every environment rather than only production, every session reaches a queue that can accept it, conversations attach to a person, and the deployment survives a wildcard retrieve instead of vanishing from the change set.
Core Concepts
MIAW vs Legacy Live Agent / Snap-ins
Messaging for In-App and Web (MIAW) is the current Salesforce platform for real-time and asynchronous web chat. It supersedes both legacy Live Agent (classic) and the Snap-ins SDK. Key differences:
MIAW uses the Messaging Session object instead of LiveChatTranscript. Custom reports and integrations built on LiveChatTranscript do not apply to MIAW sessions.
MIAW supports asynchronous messaging: customers can close the browser and resume the conversation later, matching the behavior of SMS and WhatsApp channels. Legacy Live Agent terminates the session on browser close.
MIAW deployments are EmbeddedServiceConfig records with deploymentFeature set to EmbeddedMessaging; the legacy Chat value on the same enum is LiveAgent. Only EmbeddedMessaging deployments carry the embeddedServiceMessagingChannel block that binds a Messaging Channel, so a deployment built with the wrong value has no channel to surface sessions from.
The Snap-ins SDK (used for mobile app chat) is a separate product and is not covered by this skill.
Messaging Channel and Embedded Service Deployment
Every MIAW implementation requires two linked records:
Messaging Channel (Setup > Messaging > Messaging Channels) — defines the channel name, routing configuration, pre-chat fields, and off-hours behavior. The API name of this record is used in the deployment snippet.
Embedded Service Deployment (Setup > Embedded Service Deployments) — generates the JavaScript snippet deployed to the website. The deployment references the Messaging Channel through embeddedServiceMessagingChannel/messagingChannel, and names the Experience site through the Required site field. UNVERIFIED (2026-09-05): the Metadata API guide shows one channel reference per deployment but does not state whether several deployments may share one channel; confirm in the org before designing a shared-channel, multi-branding layout. Each host origin needs its own CORS and CSP registration regardless.
Pre-chat is split across the two records: the parameters are declared on the Messaging Channel (standardParameters, limited to Email, FirstName, LastName and Subject; customParameters for everything else), while the form that renders them — embeddedServiceForms and its fields — lives on the Embedded Service Deployment. A form field's messagingChannelParameterType must match the parameter kind it points at. Collecting an email does not link a Contact; that identity write goes to MessagingEndUser.ContactId, not to the session.
CORS and CSP Trusted Sites
The chat widget loads cross-origin resources from Salesforce CDN endpoints. Two separate allow-lists must be updated for every domain where the widget is embedded:
CORS Trusted Sites (Setup > CORS) — permits the browser to make cross-origin API calls to the Salesforce org. Without this, the widget initialization call is blocked by the browser with a CORS error and the chat button never appears.
CSP Trusted Sites (Setup > CSP Trusted Sites) — controls which external sources the Lightning container allows. Required for the widget script tag to load at all.
Both entries carry a scheme and hostname (e.g. https://www.example.com). Both accept a wildcard, with different rules: CorsWhitelistOrigin.urlPattern supports * only in front of a second-level domain (https://*.example.com is valid, https://support.*.com is not), and CspTrustedSite.endpointUrl accepts *.example.com. The CSP record additionally grants nothing until at least one directive is set — see references/gotchas.md #8. Forgetting one of the pair, or registering production and missing preview/staging hosts, is the most common cause of widget failures outside production.
Omni-Channel Routing for Messaging Sessions
MIAW sessions route through Omni-Channel, not through a separate chat routing engine. Two routing approaches are available:
Queue-Based Routing: A Messaging Channel is associated with an Omni-Channel Queue. Sessions enter the queue and are assigned to the first available agent with the matching Presence Status and sufficient capacity. This is the simplest path and is appropriate for orgs without complex routing requirements.
Flow-Based Routing (Omni-Channel Flow): An Omni-Channel Flow (type: Messaging Session) intercepts each new session and can route conditionally — for example, routing to language-specific queues, checking business hours, or escalating to a different skill level. Required when routing logic cannot be expressed by a single queue assignment.
A fallback queue must always be configured on the Messaging Channel. If the Omni-Channel Flow fails or no agents are available, sessions fall to the fallback queue rather than dropping silently.
Status-Based Capacity Model
Salesforce recommends Status-Based capacity for MIAW orgs. Under this model:
Each agent Presence Configuration defines a numeric capacity (e.g., 5 messaging sessions).
The platform tracks open Messaging Sessions against that capacity.
When an agent marks a session resolved or transfers it, capacity is immediately released.
The older Tab-Based model releases capacity when the console work tab closes, which is unreliable for asynchronous messaging that outlives a tab. There are two switches: the org-level gate OmniChannelSettings.enableOmniStatusCapModel, and — from API version 65.0 — a per-channel ServiceChannel.capacityModel of STATUS_BASED or TAB_BASED. Both must agree. Capacity numbers themselves live on Presence Configurations, which belong to admin/omni-channel-routing-setup.
Common Patterns
Pattern: Greenfield MIAW Deployment with Queue Routing
When to use: New orgs or orgs migrating from legacy Live Agent that need a straightforward chat channel without complex conditional routing.
How it works:
Enable Messaging for In-App and Web in Messaging Settings.
Create a Messaging Channel: masterLabel, messagingChannelTypeEmbeddedMessaging, sessionHandlerTypeQueue, and sessionHandlerQueue naming the queue. Add the automatedResponses set for the initial and agent-end paths.
Declare pre-chat parameters on the channel — standardParameters for First Name, Last Name and Subject, customParameters for anything else.
Create the Embedded Service Deployment: site, deploymentFeatureEmbeddedMessaging, deploymentTypeWeb, the full embeddedServiceMessagingChannel block, the embeddedServiceForms pre-chat form matching step 3, and branding naming a BrandingSet.
Add CORS and CSP Trusted Site entries for the target domain.
Copy the deployment code snippet and paste into the website <head> or tag manager.
Test in a sandbox using a second browser session as the customer.
Why not the alternative: Skipping the Messaging Channel / Embedded Service Deployment separation and attempting to reuse a legacy Live Agent Chat deployment produces a deployment that routes to LiveChatTranscript, not MessagingSession, breaking all MIAW-native features.
Pattern: Flow-Based Routing with Business-Hours Fallback
When to use: Orgs with multiple agent skill groups, time-zone-aware routing, or off-hours deflection to a bot or knowledge article.
How it works:
Create an Omni-Channel Flow (type: Messaging Session).
In the flow, use a Get Records element to check Business Hours.
Branch: during hours, route to the appropriate queue based on a pre-chat field (e.g., language or product line); outside hours, send an auto-response message and end the session or queue to a low-priority fallback queue.
On the Messaging Channel, set sessionHandlerType to Flow, sessionHandlerFlow to the flow's API name, and sessionHandlerQueue to the queue that catches whatever the flow cannot place — that queue is the fallback, and it is Required.
Map each pre-chat parameter to the flow with actionParameterMappings/actionParameterName set to the flow's API name; an unmapped parameter never reaches the routing logic.
Test the routing logic in sandbox across each branch and at least one out-of-hours run, then confirm with the MessagingSession query in references/metadata-examples.md.
Why not the alternative: Implementing business-hours logic in Apex triggers on MessagingSession is possible but fragile — it fires after routing has already begun and can leave sessions in inconsistent states.
Decision Guidance
Situation
Recommended Approach
Reason
Single-language support team, no conditional routing
Queue-Based Routing
Simpler to configure and maintain; sufficient for most SMB deployments
Multi-language or multi-skill routing required
Omni-Channel Flow routing
Flows support conditional branching; queues cannot conditionally route
New org, no existing chat infrastructure
MIAW with STATUS_BASED capacity on the messaging service channel
Async sessions outlive a console tab, which is exactly what TAB_BASED keys on
Migrating from legacy Live Agent
Create new MIAW channel; run in parallel during cutover
Reusing Live Agent deployments causes transcript and routing conflicts
Pre-chat answers must reach routing logic
Declare the parameter on the channel and map it with actionParameterMappings
An unmapped parameter is stored but invisible to the flow
The conversation must attach to a Contact
Write MessagingEndUser.ContactId from a flow or trigger
MessagingSession.EndUserContactId is not writeable
Agents handle both phone and messaging
Status-Based capacity model
Tab-Based is unreliable for voice + async messaging concurrency
Recommended Workflow
Read the shapes before opening Setup. Open references/metadata-examples.md for the five types this skill owns (MessagingChannel, EmbeddedServiceConfig, BrandingSet, CorsWhitelistOrigin, CspTrustedSite), their required fields, and the deploy order. Answer the Questions to Ask table above; each unanswered row becomes a gotcha later.
Confirm the two prerequisites this package depends on but does not own. The Experience site or website named by EmbeddedServiceConfig.site must exist (admin/experience-cloud-site-setup), and the queue named by sessionHandlerQueue must exist with a routing configuration and members (admin/omni-channel-routing-setup). Retrieve both into the working project so the checker can resolve the pointers.
Author the Messaging Channel. Set messagingChannelType to EmbeddedMessaging, pick sessionHandlerType, and fill sessionHandlerQueue — it is Required and it is the fallback when sessionHandlerFlow is set. Declare pre-chat parameters (standardParameters / customParameters) and the automatedResponses set for the initial, inactive and agent-end paths.
Author the Embedded Service deployment. Set site, deploymentTypeWeb, deploymentFeatureEmbeddedMessaging, and the whole embeddedServiceMessagingChannel block — every field in it is Required. Build the pre-chat form under embeddedServiceForms, matching each formField to a parameter declared in step 3. Point branding at a BrandingSet, never at an EmbeddedServiceBranding.
Register every origin as a CORS + CSP pair. One CorsWhitelistOrigin and one CspTrustedSite per host, production and non-production, with the CSP directives explicitly set rather than defaulted. Name each EmbeddedServiceConfig explicitly in package.xml — the type does not support the * wildcard.
Gate it locally, then deploy. Run python3 scripts/check_messaging_and_chat_setup.py --manifest-dir <retrieved-tree> and clear every ERROR; the checks cover required fields, enum values, routing-pointer resolution, pre-chat form shape rules, directive-free CSP records and legacy Chat metadata mixed in. Then sf project deploy validate before sf project deploy start.
Verify with data, not with the widget. Run a test conversation, then the MessagingSession and MessagingEndUser queries in references/metadata-examples.md § Verify after deploy. Work the Review Checklist below and record which pre-chat parameters actually arrived and whether a MessagingEndUser.ContactId was written.
Review Checklist
Run through these before marking work in this area complete:
messagingChannelType is EmbeddedMessaging (or Custom for Bring Your Own Channel), not a third-party value the metadata type does not provision
sessionHandlerType matches what is actually populated: Flow with a sessionHandlerFlow, or Queue with none
sessionHandlerQueue names a real queue that has a routing configuration and at least one agent who can go available on the messaging service channel
EmbeddedServiceConfig.site resolves, and deploymentFeature is EmbeddedMessaging with deploymentTypeWeb
The whole embeddedServiceMessagingChannel block is present — every field in it is Required
Every pre-chat form field maps to a parameter declared on the channel, with a matching messagingChannelParameterType
Hidden pre-chat fields have displayOrder-1, isRequiredfalse, and a Custom parameter type
automatedResponses cover the initial, inactive and agent-end paths, with responseTimeoutInMins inside 5–60
Every widget host has both a CorsWhitelistOrigin and a CspTrustedSite, production and non-production
Each CspTrustedSite sets the directives it needs explicitly and uses Communities or All context for an Experience Cloud host
Branding is a BrandingSet referenced by EmbeddedServiceConfig.branding, not an EmbeddedServiceBranding record
package.xml names each EmbeddedServiceConfig explicitly rather than using *
python3 scripts/check_messaging_and_chat_setup.py --manifest-dir <tree> reports no ERROR
A test conversation produces a MessagingSession with ChannelType = 'EmbeddedMessaging' that reaches Ended, and a MessagingEndUser with the expected ContactId
Salesforce-Specific Gotchas
Ten non-obvious platform behaviours, each with a source line, are in references/gotchas.md. The five that most often decide whether a first deployment works:
A non-allowlisted origin returns HTTP 404, not a CORS error — so the network tab blames the URL in the snippet while the real fault is a missing CorsWhitelistOrigin. CORS does accept a wildcard, but only immediately before a second-level domain.
EmbeddedServiceConfig does not support the * wildcard in package.xml — a wildcard manifest retrieves the channel and the trusted sites and silently omits the deployment, with no error.
MessagingSession.EndUserContactId is read-only — automation that tries to stamp a Contact on the session cannot succeed; the writeable field is MessagingEndUser.ContactId.
MIAW branding uses BrandingSet — EmbeddedServiceBranding "works only with the legacy chat products", so branding deploys against it succeed and change nothing.
Every CspTrustedSite directive defaults to false — an active trusted site with no isApplicableTo* field set grants nothing, and API 59.0+ now rejects that shape while older records survive.
The web deployment: site, deploymentFeature, branding, the messaging-channel binding, and the pre-chat form
BrandingSet XML
Colours for the MIAW chat window, referenced by EmbeddedServiceConfig.branding
CorsWhitelistOrigin entries
One per widget host; without it the org answers 404
CspTrustedSite entries
One per widget host, with the needed directives explicitly set
Deployment code snippet
Copied from the Embedded Service Deployment record in Setup
Checker run output
scripts/check_messaging_and_chat_setup.py --manifest-dir <tree>, no ERROR
Verification query results
MessagingSession and MessagingEndUser rows from a test conversation
Reference Files
File
Read it when
references/metadata-examples.md
Authoring or deploying the channel, deployment, branding set and trusted sites — required fields, enum values, deploy order, package.xml, and the verification SOQL
references/gotchas.md
Something deployed cleanly and does not work, or before committing to a shape you have not deployed before
references/examples.md
Walking a greenfield build or a flow-routed build end to end, and for the CORS/CSP anti-pattern
references/well-architected.md
Justifying the routing, capacity and channel-granularity choices in a design review
references/llm-anti-patterns.md
Reviewing AI-generated messaging configuration before it reaches an org
templates/messaging-and-chat-setup-template.md
Recording context, decisions and sign-off for one deployment
Related Skills
admin/omni-channel-routing-setup — Service channels, presence statuses and configurations, queue routing configurations: everything the queue named in sessionHandlerQueue depends on
admin/experience-cloud-site-setup — The Experience site or website that EmbeddedServiceConfig.site must name
admin/service-console-configuration — The console the agent actually answers in, once sessions route
admin/business-hours-and-holidays — The Business Hours record referenced by embeddedServiceMessagingChannel/businessHours and by off-hours routing logic
agentforce/agent-channel-deployment — When an Agentforce agent, not a human queue, handles the conversation
architect/multi-channel-service-architecture — Architectural guidance when messaging is one of several concurrent channels
Use this skill when writing test-first, behavior-driven acceptance criteria in Given/When/Then format for a Salesforce user story. Covers happy path, edge cases, negative paths, permission boundaries, and data-state preconditions so the AC block can drive UAT scripts and Apex test design downstream. Trigger keywords: given when then, gherkin, behavior driven AC, test first acceptance criteria, scenario outline, BDD acceptance criteria. NOT for the user-story format itself (use admin/user-story-writing-for-salesforce). NOT for UAT script writing (use admin/uat-test-case-design). NOT for Apex test method generation (use agents/test-class-generator). NOT for high-level UAT planning (use admin/uat-and-acceptance-criteria). More triggers: acceptance criteria record, ac_id, req_id on a criterion, artefact under test, negative case for a rule-type requirement, no-match fall-through criterion, oracle for a Then clause, test type apex flow manual, criteria linter, check_ac_format.py, --manifest-dir.
Task and Event objects: polymorphic WhatId/WhoId, Activity object model, ActivityHistory vs OpenActivity, activity timeline customization, bulk task creation, Einstein Activity Capture boundaries. NOT for turning on EAC or calendar sync — use admin/einstein-activity-capture-setup. NOT for Email-to-Case — use admin/case-management-setup. Covers ActivitiesSettings metadata, enableActivities, shared Task/Event custom fields and FLS, TaskStatus value sets, Shared Activities and TaskRelation/EventRelation, recurring and child activities, and partial-success bulk DML on Task.
Design Invocable Apex actions that return deterministic, agent-friendly errors instead of surfacing raw exceptions to the LLM. NOT for authoring the action itself — @InvocableMethod schema, labels, security context — use agentforce/custom-agent-actions-apex. NOT for the Apex tests that force each error branch — use agentforce/agent-action-unit-tests.
Use when designing how an Agentforce agent extracts structured input parameters (slots) from a user's natural-language utterance to invoke an action. Triggers: 'agent extract date from user', 'agent action argument extraction', 'agentforce slot filling', 'agent invocable input mapping', 'agent fails to fill required parameter'. NOT for action authoring (use agentforce/agent-actions) or for prompt-template variable binding (use agentforce/prompt-builder-templates).
Apex test patterns for @InvocableMethod agent actions: per-reason-code branch coverage, bulk safety, callout mocking, deterministic assertions. NOT for testing whether the agent routes to the right topic or answers well — use agentforce/agent-testing-and-evaluation. NOT for designing the error envelope the tests assert on — use agentforce/agent-action-error-handling.
Designing or reviewing Agentforce actions: Flow actions, Apex invocable actions, prompt-template actions, action naming, input and output contracts, confirmation requirements, and safe error behavior. Triggers: 'agent actions', 'build an agent that can look up an order', 'make the agent able to check order status', 'give the agent a capability', 'agent that can retrieve a record', 'flow action for agent', 'agent invocable action', 'action schema design'. NOT for writing the Apex class itself — use agentforce/custom-agent-actions-apex. NOT for the reason_code error envelope that stops an agent retry loop — use agentforce/agent-action-error-handling.