
What if your sales team could ask a Copilot to check contract status in your ERP, pull support tickets from your ITSM system, or analyze energy consumption trends from your data lake—all without leaving Outlook or Teams? That’s the promise of extending Copilot for Sales, and it’s finally within reach.
Over the past year, I’ve been deeply immersed in Copilot for Sales—testing its capabilities, understanding its limitations, and eagerly awaiting the extensibility features that would unlock its full potential. When Microsoft announced the ability to extend Copilot for Sales through Copilot Studio (still in preview), I knew this was a game-changer. I’ve since delivered community sessions to evangelize these capabilities, and now I’m documenting everything in this comprehensive guide, so everyone can access this knowledge—whether you attended those sessions or are discovering this for the first time.
By the end, you’ll have a working example and the confidence to tailor Copilot for Sales to your organization’s unique needs.
Before we dive into building custom extensions, let’s establish a solid foundation. Understanding how Copilot for Sales works, what it connects to, and where it fits in the Microsoft ecosystem is crucial for successful extensibility.
Copilot for Sales is Microsoft’s AI-powered sales assistant designed to work within the tools sellers already use daily—Outlook, Teams, Word, and other Microsoft 365 applications. Think of it as a roles-based layer that makes Microsoft 365 “speak Sales,” transforming generic productivity tools into specialized sales instruments. At its core, Copilot for Sales is an AI assistant that empowers sellers with insights, recommendations, and automation capabilities, all while staying connected to your CRM. It surfaces relevant customer information, suggests next steps, generates email summaries, prepares meeting recaps, and helps update CRM records—without forcing sellers to context-switch between applications.
The key integration points include:
Copilot for Sales delivers its capabilities through multiple experience types, each designed for different interaction patterns:

As shown in the diagram, there are four primary experience modalities:
These experiences work together seamlessly, giving sellers the flexibility to interact with Copilot in the way that best suits their workflow at any given moment.
The power of Copilot comes from its ability to intelligently orchestrate data from multiple sources and present it in context. What makes this particularly powerful is the symbiotic relationship between Microsoft 365 Copilot and Copilot for Sales. When you ask Microsoft 365 Copilot a sales-related question—for example, “Create a proposal document for this customer highlighting our solutions that address their recent support issues”—here’s what happens:

It means that custom extensions you build aren’t isolated—they become part of the broader Microsoft 365 Copilot intelligence (if you have both). When you extend Copilot for Sales with a connector to your ERP or data lake, that data can become available not just in the Copilot For Sales side pane, but also when Microsoft 365 Copilot is generating documents and using Copilot For Sales, preparing presentations, or drafting emails.
Here’s where things get exciting. Out of the box, Copilot for Sales connects to your CRM and Microsoft 365 data. But what about all those other critical business systems—your ERP, your ITSM platform, your custom data lake, your specialized industry applications?
This is where Microsoft Copilot Studio (MCS) and the extensibility framework come into play. Through Copilot Studio, you can:

One of the most common points of confusion is understanding the difference between “Copilot for Sales” and “Copilot in Sales.” While the names sound similar, they address different use cases and are complementary within the Dynamics 365 ecosystem.

Their common goal is to bring AI-powered features to you, no matter where you work. Whether you’re crafting an email in Outlook or reviewing an opportunity in Dynamics 365, Copilot is there to assist.
Let’s ground this in practical examples. With custom extensions, you could enable scenarios like:
These aren’t futuristic possibilities—they’re achievable today with the extensibility framework we’ll explore in this guide.
Now that we understand what Copilot for Sales is and how it leverages data from multiple sources, let’s explore how extensibility actually works. This chapter breaks down the architecture, explains what happens when you add an extension, and provides practical guidance for makers looking to enhance Copilot for Sales with custom capabilities.
Extension points are the integration hooks where you can inject custom functionality into Copilot for Sales. Think of them as designated places in the user experience where your custom data and actions can surface.

As of today, you can enhance the following capabilities through custom extensions:
Let’s demystify the architecture. When you extend Copilot for Sales, you’re creating a bridge between the AI experiences and your external data sources. Here’s how all the pieces fit together:

At the top, we have the Microsoft 365 applications where sellers work: PowerPoint, OneNote, Excel, Planner, Outlook, Teams, and Word. These apps host different Copilot for Sales experiences:
The Middle Layer is where Copilot for Sales orchestrates and integrates its various capabilities, known as “skills:
The CRM Sales Entity Skills layer is where your custom actions and data integrations become available to the AI. The Bottom Layer represents the underlying data sources and AI-driven intelligence:
Notice the arrows connecting the layers. When a seller interacts with Copilot—whether asking a question in chat, viewing a record summary, or drafting an email—the system:
Extensions developed through Copilot Studio become first-class data sources in this architecture. They’re queried alongside your CRM and Microsoft 365 data, enabling truly comprehensive AI responses. We can either develop our own extensions or leverage third-party ones to enhance the out-of-the-box Copilot for Sales skills with custom data.
Let’s see a popular third-party extension available in the marketplace. The DocuSign extension provides five actions that extend the Sales Chat Experience:

When you add an extension to Copilot for Sales through Copilot Studio, several components are automatically created in your Power Platform environment. Understanding these components helps you manage, troubleshoot, and govern your extensions effectively.

The simple diagram shows how these components relate to each other. The AI Copilot is the central hub, connecting to AI Plugins, which contain AI Plugin Operations. Those operations have parameters that define their inputs, and conversation starters (with their mappings) provide easy entry points for users. Understanding these components is important because it will helps you especially when you want to delete some of these components (as it’s not possible to do it in Copilot Studio).
Before you dive into building your own extensions, here are some essential tips to ensure a smooth development and deployment experience:
You now understand the architecture of Copilot for Sales extensibility, how extension points work, what components are created in your solution, and the key considerations for makers. This foundation prepares you for the next chapter, where we’ll get hands-on.
In the Hands-On: Building Custom Extensions chapter, we’ll walk through the complete process of creating a custom extension from scratch—selecting a data source, building a custom connector, configuring the plugin, mapping conversation starters, and testing the experience end-to-end.
Theory is valuable, but nothing beats building something real. In this chapter, we’ll walk through the complete process of creating a custom extension for Copilot for Sales—from building an API backend to configuring the connector, adding actions to the Copilot agent, and seeing it work in action. By the end of this walkthrough, you’ll have created a working extension and understand exactly how to replicate this process for your own organization’s unique data sources and business requirements.
Before we start building, it’s critical to understand the requirements and expectations that Copilot for Sales has for custom extensions. Getting these details right is the difference between an extension that works seamlessly and one that fails to trigger or display results.

Accurate descriptions are absolutely critical—if they’re unclear or missing, your actions may never trigger or never discovered at all, leading to wasted time troubleshooting. The best way to avoid this is to reuse the Swagger example provided by Microsoft , ensuring all required descriptions, inputs, and outputs are properly in place from the start. This approach saves effort and guarantees alignment with Copilot for Sales expectations.
For our proof-of-concept, we’ll implement two extension points that demonstrate different capabilities. Let’s examine what inputs and outputs Copilot for Sales expects for each.
Goal: Enhance Copilot for Sales by providing additional sales insights from your application to enrich CRM record summaries with relevant context for meetings and emails. This extension point allows you to surface contextual information when a seller views an opportunity, account, or contact record.
Expected Inputs (What Copilot sends to your API):
{
"recordType": "opportunity", // Can be opportunity, account, contact
"recordId": "123e4567-e89b-12d3-a456-426614174000",
"startDateTime": "2025-05-01T08:00:00Z",
"endDateTime": "2025-05-24T17:00:00Z",
"top": 10,
"skip": 0,
"crmType": "Dynamics 365", // Can be Salesforce
"crmOrgUrl": "contoso.crm.dynamics.com"
}
Key fields explained:
Expected Outputs (What your API returns):
{
"value": [
{
"title": "Contract signed",
"description": "You have 5 connections in Fourth Coffee Inc",
"category": "2024-05-07T03:28:38Z",
"url": null,
"additionalProperties": {
"Contract name": "50 Cafe-A-100 Automatic Renewal Contract",
"Signed by": "Alberto Burgos, Tony",
"Signed": "2023-09-07"
}
}
]
}
Key fields explained:
Goal: Extend Copilot for Sales by providing related records from your application to enrich CRM contact details with additional contextual insights. This extension point allows you to show related documents, contracts, support tickets, or other records when viewing contact or account details.
Expected Inputs (Same structure as record summaries):
{
"recordType": "opportunity",
"recordId": "123e4567-e89b-12d3-a456-426614174000",
"top": 10,
"skip": 0,
"crmType": "Dynamics 365",
"crmOrgUrl": "contoso.crm.dynamics.com"
}
Expected Outputs:
{
"value": [
{
"recordId": "ID1",
"recordTypeDisplayName": "Contract",
"recordTitle": "50 Cafe-A-100 Automatic Renewal Contract",
"recordTypePluralDisplayName": "Documents",
"recordType": "contract",
"url": "https://contosohub.com/contract/id1",
"additionalProperties": {
"Status": "Signed",
"Date": "9/7/23",
"Signed by": "Alberto Burgos, Tony [last name]"
}
}
],
"hasMoreResults": false
}
Key fields explained:
Notice the difference: record summaries provide insights and observations (what’s notable about this record), while record details provide related entities and documents (what’s connected to this record).
Now that we understand what Copilot for Sales expects, let’s build the API that will serve as our extension’s backend. For this guide, I’ve created an ASP.NET Core Web API that implements the extension points we discussed earlier. The complete source code is available on GitHub: allandecastro/CopilotForSalesExtension.
⚠️ Note: : This is a proof-of-concept implementation. Authentication is not covered here—in a production scenario, you’d secure your API with Entra Id.
The structure follows a clean separation of concerns: Controllers handle HTTP requests, Models/Requests define the input contracts from Copilot for Sales, and Models/Responses define exactly what Copilot expects back.
CopilotExtension.Custom/
├── Controllers/
│ ├── CRMRecordDetailsController.cs # Extension: Enrich record details
│ ├── CRMRecordSummaryController.cs # Extension: Enrich record summaries
│ ├── EmailSummaryController.cs # Extension: Enhance email summaries
│ ├── EmailSummaryV2Controller.cs
│ └── KeySalesInfosController.cs # Extension: Key sales information
├── Models/
│ ├── Requests/
│ │ ├── ActivitiesRequest.cs
│ │ ├── EmailInsightRequest.cs
│ │ ├── EmailInsightsRequest.cs
│ │ ├── KeySalesInfosRequest.cs
│ │ └── RecordDetailsRequest.cs
│ └── Responses/
│ ├── ActivityListResponseEnvelope.cs
│ ├── EmailInsightListResponse.cs
│ ├── EmailSummaryResponse.cs
│ ├── ExternalRelatedRecordListResponseEnvelope.cs
│ └── SalesHighlightListResponseEnvelope.cs
├── Program.cs
├── appsettings.json
└── appsettings.Development.json
The most critical aspect of building a Copilot for Sales extension is strict compliance with Microsoft’s expected input/output contracts. If your API doesn’t match exactly what Copilot for Sales expects, your extension simply won’t work—and you’ll spend hours debugging why.
My approach was straightforward: start from Microsoft’s sample Swagger definition and build my models to match it exactly. This ensures all required fields are present, field names match precisely (case-sensitive!), data types align with expectations, and response structures are properly formatted.
The Program.cs configures everything we need: controllers, Swagger generation, JSON serialization, and Application Insights for monitoring:
public static class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Add Application Insights telemetry
builder.Services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];
options.EnableAdaptiveSampling = false;
});
// Configure controllers with camelCase JSON serialization
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Copilot For Sales Extension v1",
Version = "v1"
});
// Include XML comments for better Swagger documentation
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
var app = builder.Build();
// Serialize Swagger as OpenAPI 2.0 (required for Power Platform connectors)
app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Copilot For Sales Extension v1");
});
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
}
}
A few important points:
SerializeAsV2 = true: Power Platform custom connectors require OpenAPI 2.0 (Swagger) format, not OpenAPI 3.0. This setting ensures compatibility.Once your API is ready, deploy it to Azure App Service using Visual Studio, VS Code, GitHub Actions, or Azure DevOps.
After deployment, verify the Swagger endpoint is accessible at: https://your-copilot-extension-api.azurewebsites.net/swagger

Before connecting to Copilot for Sales, test your API independently using the Swagger UI or tools like Postman:
GET https://your-api.azurewebsites.net/api/enhanceskills/related-records
?recordType=opportunity
&recordId=123e4567-e89b-12d3-a456-426614174000
&top=10
&skip=0
&crmType=Dynamics%20365
&crmOrgUrl=contoso.crm.dynamics.com
Verify the response matches the expected structure before proceeding to Step 2.
💡 Pro Tip: Use the
.httpfile in the project (CopilotExtension.Custom.http) to quickly test your endpoints directly from VS Code with the REST Client extension.
Even though we decorated our API with OpenAPI attributes and XML comments, we still need to create a custom connector manually. Why? Because the exact names and descriptions of inputs, outputs, and actions are critical for Copilot for Sales to discover and trigger your extensions correctly.
The safest approach is to create a fresh connector and directly edit the Swagger definition by copying Microsoft’s sample specification. This guarantees compliance with Copilot for Sales expectations—no guessing, no debugging why your action isn’t being triggered.
💡 Pro Tip: Start from Microsoft’s sample Swagger, then adapt it to your API—not the other way around.
Perfect! Now I can see your final Swagger definition. Let me revise Step 2 to be more precise and include reference to your actual configuration:
Even though we decorated our API with OpenAPI attributes and XML comments, we still need to create a custom connector manually. Why? Because the exact names and descriptions of inputs, outputs, and actions are critical for Copilot for Sales to discover and trigger your extensions correctly.
The safest approach is to create a fresh connector and directly edit the Swagger definition by copying Microsoft’s sample specification. This guarantees compliance with Copilot for Sales expectations—no guessing, no debugging why your action isn’t being triggered.
💡 Pro Tip: Start from Microsoft’s sample Swagger, then adapt it to your API—not the other way around.
Navigate to make.powerapps.com, select your environment, your solution and go to New → Automation → Custom connectors. Give your connector a meaningful name (e.g., “Custom Copilot For Sales Extension”).
On the General tab:
/
Toggle the Swagger Editor to reveal the raw OpenAPI definition—this is where you’ll paste your customized specification. – and replace the content with your Swagger definition. The key elements to verify:
swagger: '2.0'
info:
title: Custom Copilot For Sales Extension
description: This is a sample connector to extend Copilot for Sales.
version: '1.0'
host: your-api.azurewebsites.net # ← Update this to your API host
basePath: /
schemes:
- https
Your Swagger should include the extension points you’ve implemented. Each one has a specific operationId that Copilot for Sales recognizes:
| Extension Point | Path | Operation Id | Summary |
| Record Details | /api/enhanceskills/related-records | scp-get-related-records | Enrich CRM record details |
| Record Summary | /api/enhanceskills/activities | scp-get-related-activities | Enrich CRM record summary |
| Key Sales Info | /api/enhanceskills/sales-highlights | scp-get-sales-highlights | Enrich key sales info |
⚠️ Important: The
operationIdvalues (prefixed withscp-) and the descriptions are what Copilot for Sales uses to understand your extensions. Don’t modify these unless you know exactly what you’re doing.
Microsoft’s sample Swagger contains all possible extension points. If you haven’t implemented all of them, remove the paths you don’t need to avoid confusion. Only keep the endpoints your API actually supports.
On the Security tab, configure authentication if your API requires it. For this PoC, we’re leaving it with no authentication (securityDefinitions: {}).
On the Test tab:
recordType: accountrecordId: 123e4567-e89b-12d3-a456-426614174000top: 2skip: 2crmType: Dynamics365crmOrgUrl: myorg.crm4.dynamics.comVerify you get a 200 OK response with the expected JSON structure.

Once testing passes, click Create connector. Your connector is now ready to be added to Copilot for Sales in Copilot Studio.
host from Microsoft’s sample to your own APIscp- prefixed operation IDs are recognized by Copilot for Sales—don’t change them$ref in your paths must have a corresponding entry in definitionsGET, but Email Summary V2 uses POSTNow that our API is deployed and our custom connector is ready, it’s time to connect everything to Copilot for Sales through Copilot Studio. This is where your extensions become available to sellers in Outlook and Teams.
⚠️ Prerequisites: I assume Copilot for Sales is already deployed in your tenant. If not, you’ll need to deploy it first before proceeding.
Go to copilotstudio.microsoft.com and ensure you’ve selected the correct environment—the same one where you created your custom connector. In Copilot Studio, locate and open the Copilot for Sales agent. This is the pre-built agent that Microsoft provides, which we’ll extend with our custom actions.
Within the Copilot for Sales agent, navigate to the section where you can add tools (connectors). Add your custom connector—the one we created in Step 2.

For each action in your connector, you’ll need to add it to the agent. As you add each action, verify that:



The actions you’ll be adding correspond to the extension points we implemented as part of our API.
Once all actions are configured, save your changes and publish the agent. This makes your extensions available to users.
After publishing, you need to enable the connector and define who can use it. Go to Manage and enable your plugins by providing access to it. You have several options:

For initial testing, I recommend starting with a small group or just yourself before rolling out to the entire organization.
⏱️ Note: As mentioned in the Maker’s Tips earlier, changes may take up to 12 hours to be reflected in Copilot for Sales experiences. Be patient during your first deployment!
Open Outlook and navigate to an email thread with a contact that exists in your CRM—ideally one linked to both an Account and an Opportunity. Then open the Copilot for Sales side pane and verify that:
If you found this guide helpful, I’d love to hear what extensions you’re planning to build—drop a comment below or connect with me to share your Copilot for Sales journey.
Original Post https://www.blog.allandecastro.com/extending-copilot-for-sales-with-copilot-studio-a-practical-guide-to-custom-extensions/






