Home / admin / messaging-and-chat-setup
messaging-and-chat-setup
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.
Open the skill folder on GitHub
Messaging and Chat 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
MessagingChannelmetadata 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 noMessagingSettingsmetadata 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.enableOmniStatusCapModeland the per-channelServiceChannel.capacityModel. Both, and the capacity numbers on Presence Configurations, belong toadmin/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 onLiveChatTranscriptdo 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
EmbeddedServiceConfigrecords withdeploymentFeatureset toEmbeddedMessaging; the legacy Chat value on the same enum isLiveAgent. OnlyEmbeddedMessagingdeployments carry theembeddedServiceMessagingChannelblock 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 Requiredsitefield. 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, andsessionHandlerQueuenaming the queue. Add theautomatedResponsesset for the initial and agent-end paths. - Declare pre-chat parameters on the channel —
standardParametersfor First Name, Last Name and Subject,customParametersfor anything else. - Create the Embedded Service Deployment:
site,deploymentFeatureEmbeddedMessaging,deploymentTypeWeb, the fullembeddedServiceMessagingChannelblock, theembeddedServiceFormspre-chat form matching step 3, andbrandingnaming aBrandingSet. - 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
sessionHandlerTypetoFlow,sessionHandlerFlowto the flow's API name, andsessionHandlerQueueto 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/actionParameterNameset 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
MessagingSessionquery inreferences/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.mdfor 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.sitemust exist (admin/experience-cloud-site-setup), and the queue named bysessionHandlerQueuemust 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
messagingChannelTypetoEmbeddedMessaging, picksessionHandlerType, and fillsessionHandlerQueue— it is Required and it is the fallback whensessionHandlerFlowis set. Declare pre-chat parameters (standardParameters/customParameters) and theautomatedResponsesset for the initial, inactive and agent-end paths. - Author the Embedded Service deployment. Set
site,deploymentTypeWeb,deploymentFeatureEmbeddedMessaging, and the wholeembeddedServiceMessagingChannelblock — every field in it is Required. Build the pre-chat form underembeddedServiceForms, matching eachformFieldto a parameter declared in step 3. Pointbrandingat aBrandingSet, never at anEmbeddedServiceBranding. - Register every origin as a CORS + CSP pair. One
CorsWhitelistOriginand oneCspTrustedSiteper host, production and non-production, with the CSP directives explicitly set rather than defaulted. Name eachEmbeddedServiceConfigexplicitly inpackage.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. Thensf project deploy validatebeforesf project deploy start. - Verify with data, not with the widget. Run a test conversation, then the
MessagingSessionandMessagingEndUserqueries inreferences/metadata-examples.md§ Verify after deploy. Work the Review Checklist below and record which pre-chat parameters actually arrived and whether aMessagingEndUser.ContactIdwas written.
Review Checklist
Run through these before marking work in this area complete:
- [ ]
messagingChannelTypeisEmbeddedMessaging(orCustomfor Bring Your Own Channel), not a third-party value the metadata type does not provision - [ ]
sessionHandlerTypematches what is actually populated:Flowwith asessionHandlerFlow, orQueuewith none - [ ]
sessionHandlerQueuenames a real queue that has a routing configuration and at least one agent who can go available on the messaging service channel - [ ]
EmbeddedServiceConfig.siteresolves, anddeploymentFeatureisEmbeddedMessagingwithdeploymentTypeWeb - [ ] The whole
embeddedServiceMessagingChannelblock 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 aCustomparameter type - [ ]
automatedResponsescover the initial, inactive and agent-end paths, withresponseTimeoutInMinsinside 5–60 - [ ] Every widget host has both a
CorsWhitelistOriginand aCspTrustedSite, production and non-production - [ ] Each
CspTrustedSitesets the directives it needs explicitly and usesCommunitiesorAllcontext for an Experience Cloud host - [ ] Branding is a
BrandingSetreferenced byEmbeddedServiceConfig.branding, not anEmbeddedServiceBrandingrecord - [ ]
package.xmlnames eachEmbeddedServiceConfigexplicitly rather than using* - [ ]
python3 scripts/check_messaging_and_chat_setup.py --manifest-dir <tree>reports no ERROR - [ ] A test conversation produces a
MessagingSessionwithChannelType = 'EmbeddedMessaging'that reachesEnded, and aMessagingEndUserwith the expectedContactId
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. EmbeddedServiceConfigdoes not support the*wildcard inpackage.xml— a wildcard manifest retrieves the channel and the trusted sites and silently omits the deployment, with no error.MessagingSession.EndUserContactIdis read-only — automation that tries to stamp a Contact on the session cannot succeed; the writeable field isMessagingEndUser.ContactId.- MIAW branding uses
BrandingSet—EmbeddedServiceBranding"works only with the legacy chat products", so branding deploys against it succeed and change nothing. - Every
CspTrustedSitedirective defaults tofalse— an active trusted site with noisApplicableTo*field set grants nothing, and API 59.0+ now rejects that shape while older records survive.
Output Artifacts
| Artifact | Description |
|---|---|
MessagingChannel XML | Channel type, routing (sessionHandlerType / sessionHandlerFlow / sessionHandlerQueue), embeddedConfig, auto-responses, pre-chat parameters |
EmbeddedServiceConfig XML | 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
sessionHandlerQueuedepends on - admin/experience-cloud-site-setup — The Experience site or website that
EmbeddedServiceConfig.sitemust 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/businessHoursand 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