사용 방법
- 설치 명령을 복사하고 자신의 에이전트 환경에 설치하기 전에 소스 패키지를 확인하세요.
- 필요한 도구, 인증 정보, 지원 환경은 원본 문서에서 확인하세요.
- 입력과 원하는 산출물을 설명하고 출처와 결과를 대조하세요.
npx skills add https://github.com/sshvarzman-salesforce/claude-skills --skill routing-inbound-conversations-to-asa시작 전 확인
- 설치에는 모델 구독, API 한도, 파일 접근 권한이 포함되지 않습니다.
- 별점 수와 설치 수는 활동 지표이며 독립적인 품질·보안 검증이 아닙니다.
원본 저장소의 제목, 예시, 코드, 표, 링크, 이미지를 유지해 표시합니다.
Routing inbound conversations to an Agentforce Service Agent (ASA)
What this skill is for
An inbound RoutingFlow is what Omni-Channel runs when a new VoiceCall or MessagingSession arrives, to decide who picks it up. To route the conversation to an Agentforce Service Agent (ASA — the bot/copilot answers first, before any human), the flow's routeWork action uses `routingType=Copilot` and points at the agent's BotDefinition — plus a REQUIRED fallback queue.
This is the mirror image of the replicating-omnichannel-routing-flows skill:
- That skill = escalation (agent/bot → human queue),
routingType=QueueBased, invoked via@utils.escalate. - This skill = inbound (channel → ASA agent),
routingType=Copilot, invoked by the channel's inbound-flow assignment.
Two flows, one per channel: a Voice variant (VoiceCall, sfdc_phone, links VoiceCall.Contact__c) and a Messaging variant (MessagingSession, sfdc_livemessage, links MessagingEndUser.ContactId).
Ready-to-edit, verified templates ship in assets/:
assets/inbound-asa-voice.flow-meta.xmlassets/inbound-asa-messaging.flow-meta.xml
Both are ground-truthed from the live, Active CitiBank flows CitiBank_ASA_Inbound_Voice / CitiBank_ASA_Inbound_Messaging.
THE headline lesson: routeWork to a copilot still REQUIRES a fallback queue
The single most common failure: authoring a routingType=Copilot routeWork with only the copilot target and no queue. It looks logically complete — you're routing to the agent, why name a queue? — but the platform requires a fallback `queueId` + `queueLabel` anyway. Omni needs a queue to fall back to when the agent can't take the work (capacity, offline, decline, timeout). Without it, routing fails.
So a copilot routeWork carries both:
| Purpose | Params |
|---|---|
| Primary target (the ASA agent) | routingType=Copilot, copilotId, copilotLabel |
| Required fallback | queueId (a real 00G… Group.Id), queueLabel |
| Channel | serviceChannelDevName, serviceChannelId, serviceChannelLabel |
| Work record | recordId |
If you copy an escalation flow and just "swap QueueBased for Copilot," you'll drop the queue and it breaks. Keep the queue.
The routeWork action — copilot routing, per channel
Voice (sfdc_phone)
<inputParameters><name>recordId</name><value><elementReference>recordId</elementReference></value></inputParameters>
<inputParameters><name>serviceChannelLabel</name><value><stringValue>Phone</stringValue></value></inputParameters>
<inputParameters><name>serviceChannelDevName</name><value><stringValue>sfdc_phone</stringValue></value></inputParameters>
<inputParameters><name>routingType</name><value><stringValue>Copilot</stringValue></value></inputParameters>
<inputParameters><name>copilotLabel</name><value><stringValue>CitiBank ASA</stringValue></value></inputParameters>
<inputParameters>
<name>serviceChannelId</name>
<value><setupReference>sfdc_phone</setupReference><setupReferenceType>ServiceChannel</setupReferenceType></value>
</inputParameters>
<inputParameters>
<name>copilotId</name>
<value><setupReference>CitiBank_ASA</setupReference><setupReferenceType>BotDefinition</setupReferenceType></value>
</inputParameters>
<!-- REQUIRED fallback queue -->
<inputParameters><name>queueLabel</name><value><stringValue>General Voice</stringValue></value></inputParameters>
<inputParameters><name>queueId</name><value><stringValue>00GKc000000wPROMA2</stringValue></value></inputParameters>Notable: on the voice flow the agent is expressed as a `setupReference` to `BotDefinition` (the agent's DeveloperName, e.g. CitiBank_ASA) and the channel as a setupReference to ServiceChannel. versionString = 2.0.0 on this action.
Messaging (sfdc_livemessage)
<inputParameters><name>recordId</name><value><elementReference>recordId</elementReference></value></inputParameters>
<inputParameters><name>serviceChannelId</name><value><stringValue>0N9Kc000000h7oJKAQ</stringValue></value></inputParameters>
<inputParameters><name>serviceChannelLabel</name><value><stringValue>LiveMessage</stringValue></value></inputParameters>
<inputParameters><name>serviceChannelDevName</name><value><stringValue>sfdc_livemessage</stringValue></value></inputParameters>
<inputParameters><name>routingType</name><value><stringValue>Copilot</stringValue></value></inputParameters>
<!-- REQUIRED fallback queue -->
<inputParameters><name>queueId</name><value><stringValue>00GKc000000wPRNMA2</stringValue></value></inputParameters>
<inputParameters><name>queueLabel</name><value><stringValue>Messaging</stringValue></value></inputParameters>
<inputParameters><name>copilotId</name><value><stringValue>0XxKc000000UW41KAG</stringValue></value></inputParameters>
<inputParameters><name>copilotLabel</name><value><stringValue>CitiBank ASA</stringValue></value></inputParameters>Notable difference: on the messaging flow copilotId and serviceChannelId are raw Id strings (0Xx… copilot config Id, 0N9… channel Id), NOT setupReferences. (0Xx is the messaging agent-config record, not the BotDefinition 0Xx→ query it, see below.) No versionString on this action.
Both patterns deploy and run. The voice setupReference form is more portable (resolves by dev name); the messaging raw-Id form is what Flow Builder writes for messaging. Either works as long as the Ids/dev names resolve in the target org.Full flow structure (both channels)
Start
→ resolve the phone to search on
Voice: Read_Voice_Call → decide (phone input? else VoiceCall.FromPhoneNumber)
Messaging: Load_Messaging_User (MessagingEndUser by input_record.MessagingEndUserId)
→ Load_Contact_by_Phone_Number actionType=findMatchingIndividuals, searchObject=Contact, searchFields=Phone
→ Loop_Search_Results over .contactIds → Load_Found_Contact (Contact by Id) → contact
→ Check_Found_Contact (contact != null?)
Found: link the Contact
Voice: recordUpdate VoiceCall.Contact__c = contact.Id (+ optional Customer_Demo__c stamp)
Messaging: assign MessagingEndUser.ContactId = contact.Id → Update_Messaging_User → stamp session
No: just stamp the conversation record (no contact link)
→ Route_to_ASA routeWork, routingType=Copilot, copilot target + REQUIRED fallback queue- Contact resolution is the dual-channel crux (same as everywhere): voice links the found Contact onto the custom `VoiceCall.Contact__c` lookup; messaging writes `MessagingEndUser.ContactId`, which surfaces downstream as `MessagingSession.EndUserContactId`. (See the
messaging-inbound-omniflow-patternandlinking-individuals-by-phone-in-flowsmemories/skills.) - `findMatchingIndividuals` is the standard phone→Contact matcher; loop its
.contactIdsand load the first. - The messaging flow's input variable is `input_record` (SObject MessagingSession) in addition to
recordId(String) — it readsinput_record.MessagingEndUserIdto find the MessagingEndUser. The voice flow only needsrecordId+ aphoneinput.
Build / fix procedure
- Find the ASA agent + its ids.
# BotDefinition dev name (voice setupReference form):
sf data query --target-org <ORG> --json -q "SELECT Id, DeveloperName, MasterLabel FROM BotDefinition WHERE DeveloperName='<Agent>'"For the messaging copilotId raw string, the value is the agent's messaging routing-config Id (0Xx…). The reliable way to get the exact ids/labels is to read an already-working inbound flow in the org (retrieve it) — or copy them from the voice flow's BotDefinition and let Flow Builder resolve. If unsure, author voice with the setupReference/BotDefinition form (portable) and mirror it for messaging.
- Find a fallback queue (REQUIRED).
sf data query --target-org <ORG> --json -q "SELECT Id, DeveloperName, Name FROM Group WHERE Type='Queue'"Pick the voice queue for the voice flow, the messaging queue for the messaging flow. Put its 00G… Id in queueId and its label in queueLabel. Do not omit this.
- Confirm the messaging ServiceChannel Id (messaging flow only):
sf data query --target-org <ORG> --json -q "SELECT Id, DeveloperName FROM ServiceChannel WHERE DeveloperName='sfdc_livemessage'"- Copy the matching asset into
flows/, rename to your flow's API name, and swap the<<...>>tokens. KeeproutingType=Copilot,serviceChannelDevName, and BOTH the copilot target and the fallback queue.
- Deploy Active:
sf project deploy start --metadata Flow:<YourFlowName> --target-org <ORG> --json- Wire the flow to the channel (Setup, not metadata for the assignment itself).
- Voice: the SCV/Omni voice channel's inbound flow is set in Setup (the voice channel → flow assignment). This is the flow that runs when a call lands.
- Messaging: the messaging channel (Embedded / MIAW / etc.) names its routing/inbound flow in the channel config. For metadata-managed channels the flow is referenced in the channel definition; otherwise set it in Setup.
In practice on this stack, the inbound-flow assignment per channel is Setup-side — deploying the flow Active does not by itself bind it; point the channel at it.
- Verify:
sf data query --target-org <ORG> --json -q "SELECT ApiName, ProcessType, IsActive FROM FlowDefinitionView WHERE ApiName='<YourFlowName>'"ProcessType=RoutingFlow, IsActive=true. Then place a test call / start a test chat and confirm it lands on the ASA agent, and that Contact__c / EndUserContactId got linked.
Gotchas
- Fallback queue is mandatory for `routingType=Copilot`. #1 mistake. The agent is the primary target; the queue is the required fallback. Keep both.
- Voice vs. messaging express the agent differently. Voice =
copilotIdassetupReference/BotDefinition; messaging =copilotIdas a raw0Xx…string. Don't force one form onto the other channel unless you've confirmed the id resolves. - `serviceChannelId`: voice uses a
setupReferencetoServiceChannel(sfdc_phone); messaging uses a raw0N9…channel Id string. Both alongsideserviceChannelDevName. - Messaging needs `input_record` (MessagingSession SObject) input, not just
recordId— it readsMessagingEndUserIdfrom it. - Contact link target differs:
VoiceCall.Contact__c(custom lookup) vsMessagingEndUser.ContactId(→ surfaces asMessagingSession.EndUserContactId). - No `triggerType` on Start — an inbound RoutingFlow has a bare Start with just a connector, same as escalation flows.
- `FlowDefinitionView` is a regular object — query it WITHOUT
--use-tooling-api(tooling API rejects it withINVALID_TYPE).
Relationship to other skills
- Escalation (agent → human queue):
replicating-omnichannel-routing-flows(routingType=QueueBased,@utils.escalate). - Phone→Contact matching detail:
linking-individuals-by-phone-in-flows. - Messaging EndUser→Contact link + channel wiring: the
messaging-inbound-omniflow-patternmemory.



