
In this post, we’ll explore the different extensions available for Copilot in Dynamics 365 Sales using Copilot Studio. We’ll go through the welcome message, custom Sparkle Menu groups, building your own topics, and how to use context so Copilot actually knows what record you’re working on.
Quick note: if you’re after an overview of Copilot for Sales (the one that lives in Outlook and Teams), I wrote about that already. Check out Extending Copilot for Sales with Copilot Studio — A Practical Guide to Custom Extensions for that.
This post comes from my PPCC session — I wanted to document everything for those who couldn’t make it.
Copilot in Sales comes with powerful out-of-the-box capabilities that help sellers work more efficiently:
Now, all of this is nice. But here’s the thing — with Copilot Studio, you can push it further.
Copilot in Sales works hand-in-hand with Copilot Studio. You can bring in your own data, your own prompts, your own logic. That’s what makes it interesting.
When users interact with Copilot in Sales, they encounter several distinct experiences that you can customize:

Alright, let’s get into it. First thing you need to do: add the agent to your solution.
This is step zero. Don’t skip it.

Now you can track your customizations in source control and deploy through pipelines. The usual ALM stuff.
⚠️ One thing to know: the agent is managed. You can add new topics, but you can’t touch the existing ones unless Microsoft allows it.
The welcome message is low-hanging fruit. Easy to change, nice impact.
Where to find it:

Look for the SetTextVariable action that sets Topic.WelcomeMessage. Here’s what the YAML looks like:
- kind: SetTextVariable
id: setTextVariable_cD7eF8
variable: Topic.WelcomeMessage
value: Hey {System.User.DisplayName}, the odds are in your favor 🃏! Let's deal your next move with Copilot in Sales.
Use {System.User.DisplayName} to make it personal. Add whatever message fits your org, you can even imagine displaying the next updates of your system.
This is where it gets fun. The Sparkle Menu is what users see when they click that 📘 icon. Out of the box, you get “Get info,” “Ask questions,” “Stay ahead.” But you can add your own groups.
The main idea here is to make Copilot more intuitive and more actionable — not just reactive. So, we’ve created a Custom Spark Menu organized into four key groups: Smart Insights, Action Boosters, Relationship Radar, and Next-Gen. Each of these groups focuses on specific user needs. For example, Smart Insights helps analyze data instantly, Action Boosters accelerate routine sales tasks, Relationship Radar strengthens customer connections, and Next-Gen introduces forward-looking capabilities like predictive guidance.

What’s really powerful is that every spark delivers immediate, actionable insights — all with a single click. The experience is designed to be fast, visual, and interactive. Behind the scenes, prompts are context-aware — they adapt to the user’s current workflow. That means less complexity, more productivity, and a smoother experience overall.
The Sparkle Menu runs on events. When someone clicks the Copilot icon, it fires Microsoft.PowerApps.Copilot.RequestSparks. Your custom dialog catches that event.

kind: AdaptiveDialog
beginDialog:
kind: OnEventActivity
id: main
priority: 20
eventName: Microsoft.PowerApps.Copilot.RequestSparks
You create a ParseValue action to define your groups. This stores your custom groups in Global.MyCustomSparksGroup. You define the group names, the individual sparks inside each group, icons, all of it.
- kind: ParseValue
id: XcxspA
variable: Global.MyCustomSparksGroup
valueType: Table
value: "[your JSON here]"

This is what I used in my example:
[
{
"displayName": "Smart Insights",
"displaySubtitle": "Turn data into recommendations",
"iconName": "List24Regular",
"sparks": [
{
"displayName": "Deal Pulse",
"type": "PromptText"
},
{
"displayName": "AI Forecast",
"type": "PromptText"
}
]
},
{
"displayName": "Action Boosters",
"displaySubtitle": "Reduce clicks. Close faster.",
"iconName": "List24Regular",
"sparks": [
{
"displayName": "Draft Follow-up Email",
"type": "PromptText"
},
{
"displayName": "Create Opportunity Note",
"type": "PromptText"
},
{
"displayName": "Update Stage",
"type": "EventActivity"
}
]
},
{
"displayName": "Relationship Radar",
"displaySubtitle": "Strengthen customer connections",
"iconName": "List24Regular",
"sparks": [
{
"displayName": "Customer Sentiment",
"type": "PromptText"
},
{
"displayName": "Top Stakeholders",
"type": "PromptText"
}
]
},
{
"displayName": "Next-Gen",
"displaySubtitle": "Accelerate enablement & collaboration",
"iconName": "List24Regular",
"sparks": [
{
"displayName": "Best Practices Hub",
"type": "PromptText"
},
{
"displayName": "Team Summary",
"type": "PromptText"
}
]
}
]
Without this, Copilot won’t show your groups. It needs to know what to render for this session.
- kind: SetVariable
id: setVariable_kaE01k
variable: Topic.SparkGroups
value: =Global.MyCustomSparksGroup
You probably don’t want to blow away the default sparks. Here’s how to keep them and add yours:
- kind: SetVariable
id: setVariable_9PFCUk
variable: Global.PA_Copilot_Sparks.sparkGroups
value: =ForAll(
Sequence(CountRows(Global.PA_Copilot_Sparks.sparkGroups)+CountRows(Topic.SparkGroups)),
If(
Value <= CountRows(Global.PA_Copilot_Sparks.sparkGroups),
Index(Global.PA_Copilot_Sparks.sparkGroups, Value),
Index(Topic.SparkGroups, Value - CountRows(Global.PA_Copilot_Sparks.sparkGroups))
)
)
Looks complicated, but it’s just: keep the existing ones, then append yours at the end 😊
Sometimes you need Copilot to do something specific that isn’t covered out of the box. That’s where custom topics come in. This is a good way to try to minimize action in the application by just using the chat (especially in mobility).
Let’s say your team wants to quickly add notes to opportunities through Copilot. Natural phrases like:

The note ends up in Dataverse, linked to the opportunity, with the current user as creator. Simple.
When extending Copilot in Sales, providing context ensures that Copilot understands where and what the user is working on. Context allows Copilot to tailor its responses and actions to the current record, enabling more relevant insights and suggestions. The Record Picker complements this by letting users explicitly select the record they want Copilot to focus on. Together, context passing and the record picker create a more guided, accurate, and personalized Copilot experience—helping sellers stay focused while Copilot adapts intelligently to their workflow.
You get context from two places to get your record:
You can also have detail about the app you’re using (very useful, especially when you customize your agent in an environment with multiple MDAs):
Global.PA__Copilot_Model_AppUniqueNameContext.appUniqueName
Users type “/” and get a search box. It uses Dataverse search, so if your entity isn’t showing up, you need to add it to the search index.

Useful functions when working with Record Picker:
Global.PA_Copilot_DVRecordContext
First(Global.PA_Copilot_DVRecordContext).EntityName="account"
First(Global.PA_Copilot_DVRecordContext).RecordId
CountRows(Global.PA_Copilot_DVRecordContext)<>1
Last(Global.PA_Copilot_DVRecordContext).Record
Between page context and record picker, you can build topics that actually know what the user is working on.
Let me walk through a scenario I built that ties all of this together.
When receiving a call from a customer whose contact is not yet identified in the system, the agent first validates the caller’s identity. Once confirmed, the agent needs to:
To ensure accuracy and efficiency, the Account context should be leveraged when creating the Case — either directly from the Account form or by selecting the record through the Record Picker.

The key to a seamless experience is detecting where the user’s context is coming from. There are two possible sources: the Record Picker (when users type “/” and select a record) or the Page Context (when users are viewing a specific record form). Our logic checks both scenarios in priority order.
First, we check if the user explicitly selected an account using the Record Picker. The Global.PA_Copilot_DVRecordContext variable holds any record the user selected via “/”. If that record is an account, we grab its ID immediately.
If no Record Picker selection exists, we fall back to checking the Page Context. The Global.PA__Copilot_Model_PageContext variable tells us which record form the user is currently viewing. If they’re on an Account form, we extract the entity ID from there.
If neither context source provides an account, we gracefully prompt the user to select one — ensuring the workflow never fails silently.
If(
First(Global.PA_Copilot_DVRecordContext).EntityName = "account",
// User selected an account via Record Picker - use that context
Set(varAccountId, First(Global.PA_Copilot_DVRecordContext).RecordId),
// Check if we're on an Account form
If(
Global.PA__Copilot_Model_PageContext.pageContext.entityName = "account",
Set(varAccountId, Global.PA__Copilot_Model_PageContext.pageContext.entityId),
// No account context - ask user to select one
// ... prompt user
)
)
This pattern demonstrates how to build intelligent, context-aware workflows that:
The agent can be on the Account form when they trigger this, or they can type “/” to pick the account. Either way, Copilot knows which account to link everything to.
That’s it. If you build something cool with this, let me know in the comments or hit me up on LinkedIn. Always interested to see what people are doing with Copilot. Let’s deal the next hand together! 🃏
Original Post https://www.blog.allandecastro.com/extending-copilot-in-dynamics-365-sales-with-copilot-studio-tailor-it-your-way/






