Learn how to customize Copilot in D365 Field Service (preview)
A few days ago I came across an article on learn.microsoft.com that explained how to streamline inventory visibility in Dynamics 365 Field Service by using the out of the box Copilot inside of Dynamics365 Field Service. What this allows us to do is customize the agent that lives inside of D365 Field Service in a way that when a user opens a work order, it can ask the agent to check the inventory in the connected Dynamics 365 Supply Chain Management environment based on the products that are associated with the work order (as long as inventory is tracked for the work order products). For example if I have a work order that needs a fan coil unit and a circuit breaker, the agent would check the inventory and list the quantity of these products that are in stock. Pretty cool right? I thought so! As I was thinking about this example, I realized that not every D365 Field Service implementation will have a D365 Supply Chain Management environment integrated, there might be a different ERP solution integration so I took it upon myself to create a solution for those scenarios. Keep in mind that this is an untested solution, but I hope it gives you some perspective on what we can do when it comes to configuring the agent inside D365 Field Service.
Details
With this configuration I will be adding an ‘Inventory Info’ prompt header to the prompt menu that loads on the top of the agent screen inside D365 Field Service. When users click on this, the prompt ‘Provide WO Products Inventory Info’ will load in the chat, so all a user has to do is press enter to send the message to the agent. For the actual logic I will add a topic that will check if the page the user has opened on the screen is a work order, (I can only run this logic one work order at the time.) and an agent flow that fetches the (work order) products associated with the work order. It will then compare those products to products in the product inventory table and if they are present, it will feed those totals back to the agent. A prompt inside the topic will make sure all products are totaled, regardless of the warehouse they are in. You could expand this agent flow to include each warehouse the products are in, but I kept it simple in this example. The image below shows what the end result looks like.

Add the prompt to the D365 Field Service agent
Keep in mind that this is a feature that is part of 2025 Release Wave 2, so in order to try this yourself, you’ll need to have the early access features for this wave enabled on the environment. Once that is completed you can access the agent by navigating to copilotstudio.microsoft.com and selecting the correct environment. In the list of agents, you’ll need to find the ‘Copilot in Power Apps – Field Service’ agent. In the list of topics, make a copy of the ‘Field Service Zero Prompt’ topic, then open the copied topic. NOTE: We need to make a copy first because we’re not able to make any changes to the existing ‘Field Service Zero Prompt’ topic. Below the ‘Set variable’ step for ‘OptionThree’ I add a new ‘Set Variable’ step. I name the variable ‘OptionFour’ and enter the below powerfx formula:
{sparkType: "PromptText", optionGroup: "Inventory Info", prompt: "Provide WO Products Inventory Info" }
This creates the menu item on the top of the agent. You’ll also notice that I entered the prompt “Provide WO products Inventory Info’ in the PowerFX formula. If you want the prompt to say something else, you can change that here. The last step is the ‘Set variable’ step where all the options in the prompt menu are shown in an adaptive card. I had to add the section for the custom prompt. Below is the code for the adaptive card. You can just copy and paste this into the Powerfx formula of the last step.
{
type: "AdaptiveCard",
body: [
{
type: "ColumnSet",
id: "ms-platform-zpe-columnsetheader-dc291457-0726-4472-9264-ab644fe9b13e",
columns: [
{
type: "Column",
items: [
{
type: "TextBlock",
wrap: true,
text: ""&Topic.FSHi&" "&System.User.FirstName&","
},
{
type: "TextBlock",
wrap: true,
text: ""&Topic.FSWelcomeMessage&"",
id: "ms-platform-zpe-header-74453ffe-b4e3-4c81-9a6c-5c03dc4a3661",
spacing: "Medium"
},
]
}
]
},
{
type: "Container",
id: "ms-platform-zpe-actionscontainer-d44c003a-b977-4e0b-a3fc-f8d30d60110b",
isVisible: ""&!IsBlank(Topic.OptionOne.prompt)&"",
showBorder: true,
roundedCorners: true,
selectAction: {
type: "Action.Submit",
data: {
scenario: "ZeroPromptCard",
sparkType: Topic.OptionOne.sparkType,
value: ""&Topic.OptionOne.prompt&"",
source: "ZeroPrompt"
}
},
items: [
{
type: "ColumnSet",
columns: [
{
type: "Column",
width: "auto",
items: [
{
type: "Image",
url: ""&Topic.ExclamationIcon&"",
altText: ""&Topic.OptionOne.optionGroup&""
}
]
},
{
type: "Column",
width: "stretch",
items: [
{
type: "TextBlock",
text: ""&Topic.OptionOne.optionGroup&"",
weight: "bolder"
},
{
type: "TextBlock",
text: ""&Topic.OptionOne.prompt&""
}
]
}
]
}
]
},
{
type: "Container",
id: "ms-platform-zpe-actionscontainer-477da7ce-bd52-4870-a8c2-04071daa56e2",
isVisible: ""&!IsBlank(Topic.OptionTwo.prompt)&"",
showBorder: true,
roundedCorners: true,
selectAction: {
type: "Action.Submit",
data: {
scenario: "ZeroPromptCard",
sparkType: Topic.OptionTwo.sparkType,
value: ""&Topic.OptionTwo.prompt&"",
source: "ZeroPrompt"
}
},
items: [
{
type: "ColumnSet",
columns: [
{
type: "Column",
width: "auto",
items: [
{
type: "Image",
url: ""&Topic.ClockIcon&"",
altText: ""&Topic.OptionTwo.optionGroup&""
}
]
},
{
type: "Column",
width: "stretch",
items: [
{
type: "TextBlock",
text: ""&Topic.OptionTwo.optionGroup&"",
weight: "bolder"
},
{
type: "TextBlock",
text: ""&Topic.OptionTwo.prompt&""
}
]
}
]
}
]
},
{
type: "Container",
id: "ms-platform-zpe-actionscontainer-ff8b54c0-ba5a-435f-a66f-3978115ef39a",
isVisible: ""&!IsBlank(Topic.OptionThree.prompt)&"",
showBorder: true,
roundedCorners: true,
selectAction: {
type: "Action.Submit",
data: {
scenario: "ZeroPromptCard",
sparkType: Topic.OptionThree.sparkType,
value: ""&Topic.OptionThree.prompt&"",
source: "ZeroPrompt"
}
},
items: [
{
type: "ColumnSet",
columns: [
{
type: "Column",
width: "auto",
items: [
{
type: "Image",
url: ""&Topic.OverviewIcon&"",
altText: ""&Topic.OptionThree.optionGroup&""
}
]
},
{
type: "Column",
width: "stretch",
items: [
{
type: "TextBlock",
text: ""&Topic.OptionThree.optionGroup&"",
weight: "bolder"
},
{
type: "TextBlock",
text: ""&Topic.OptionThree.prompt&""
}
]
}
]
}
]
},
{
type: "Container",
id: "ms-platform-zpe-actionscontainer-477da7ce-bd52-4870-a8c2-04071daa56e2",
isVisible: ""&!IsBlank(Topic.OptionFour.prompt)&"",
showBorder: true,
roundedCorners: true,
selectAction: {
type: "Action.Submit",
data: {
scenario: "ZeroPromptCard",
sparkType: Topic.OptionFour.sparkType,
value: ""&Topic.OptionFour.prompt&"",
source: "ZeroPrompt"
}
},
items: [
{
type: "ColumnSet",
columns: [
{
type: "Column",
width: "auto",
items: [
{
type: "Image",
url: ""&Topic.OverviewIcon&"",
altText: ""&Topic.OptionFour.optionGroup&""
}
]
},
{
type: "Column",
width: "stretch",
items: [
{
type: "TextBlock",
text: ""&Topic.OptionFour.optionGroup&"",
weight: "bolder"
},
{
type: "TextBlock",
text: ""&Topic.OptionFour.prompt&""
}
]
}
]
}
]
},
{
type: "Container",
id: "ms-platform-zpe-collapsed-footer-79fb882f-ec4e-4abd-bc65-8c523849da40",
items: [
{
type: "ColumnSet",
columns: [
{
type: "Column",
width: "auto",
items: [
{
type: "TextBlock",
wrap: true,
text: ""&Topic.FSFooterMessage&"",
id: "fsFooterId"
},
]
}
]
},
]
},
],
'$schema': "http://adaptivecards.io/schemas/adaptive-card.json",
version: "1.5"
}
Make sure you save the topic!
Create the ‘Field Service WO Product Inventory Check’ topic
The next step is to create the topic that will handle the logic when a user asks the agent to ‘Provide WO Products Inventory Info’. Navigate to topics and create the new topic from blank. Enter ‘Field Service WO Product Inventory Check’ as the topic name. For the trigger I’ll the exact phrase: ‘Provide WO Products Inventory Info’. Below the trigger I add a condition to confirm that the page that the user has opened is a work order. Under the condition search for the variable called ‘PA__Copilot_Model_PageContext.pageContext.entityTypeName’. This is a global variable, so once you select it, you should see the name Global.PA__Copilot_Model_PageContext.pageContext.entityTypeName selected. Set the condition to ‘Equal to’ and enter ‘msdyn_workorder’ as this is the system table name. Add a new condition (to make sure a single work order is shown on the page, and not a list view of work orders) and search for the variable called:’PA__Copilot_Model_PageContext.pageContext.pageType’. This is also a global variable. Again select ‘Is equal to’ then enter:’entityrecord’. On the right side of the conditions you just created, below ‘All other conditions’, add a message that says: ‘Please open a work order.’ If a user doesn’t have a work order record open they will be shown this message.

Create the agent flow
Add another step and select ‘Add a tool’, then select ‘New Agent flow’. This will open the agent flow designer. In the ‘When an agent calls the flow’ step, add a text input and enter ‘WorkorderID’. This will have the GUID of the work order which we will pass from the topic to the agent flow later. Click the + button to add an action and add the list rows (Dataverse) step. Under ‘Table name’ select the ‘Product Inventory’ table. I created a fetch XML query that will look at the product inventory rows in Dataverse and filters out the rows based on the products linked to the work order. I added a filter to only query work order products associated to the work order ID in the first step of this agent flow. Below is what the fetch XML looks like, please note you’ll need to replace ‘PLACEHOLDER WORKORDERID‘ with the dynamic value from step 1 ‘When an agent calls the flow’.
<fetch distinct="true">
<entity name="msdyn_productinventory">
<attribute name="msdyn_productinventoryid" />
<attribute name="msdyn_qtyavailable" />
<link-entity name="msdyn_workorderproduct" from="msdyn_product" to="msdyn_product" link-type="inner" alias="wop">
<filter type="and">
<condition attribute="msdyn_workorder" operator="eq" value="PLACEHOLDER WORKORDERID" />
</filter>
</link-entity>
<link-entity name="product" from="productid" to="msdyn_product" link-type="inner" alias="p">
<attribute name="productid" />
<attribute name="name" />
</link-entity>
</entity>
</fetch>

Click on the + button to add an ‘initialize variable’ step. Enter ‘varResponse’ in the name box and select ‘String’ as the type. Leave the value field blank.
Click on the + button to add an ‘Apply to each’ step. in the ‘Select an output from previous steps’ select the dynamic value ‘body/value’, which is the list of items from the ‘List rows’ step that was created earlier. Inside the ‘Apply to each’ step, click on the + button to add an ‘Append to string variable’ step. Select ‘varResponse’ from the drop down in the name field. Type the word ‘Product’ in the ‘value’ field and next to it, add the expression: items(‘Apply_to_each’)?[‘p.name’]. This will store the name of the product (the work order product has a relationship with the product table). Click ‘enter’ on your keyboard to go to the next line and type in the word ‘Available’, this will hold the value of the inventory product’s quantity. Add the expression: items(‘Apply_to_each’)?[‘msdyn_qtyavailable’] it should look like the image below.

In the last step called ‘Respond to the agent’ enter a text output. Select the output from the ‘append to string variable’ value called ‘varResponse. This will send the response back to the Field Service agent. Click the ‘Publish’ button to save and publish the agent flow, then click on the ‘Designer’ tab to change the name of the agent flow to ‘Get wo products and inventory’. You can do this by clicking the ‘edit’ button on the details section.
Add the agent flow to the topic
Navigate back to the agent and open the topics section. Find the ‘Field Service WO Product Inventory Check’ topic that was created earlier. Below the conditions that were created earlier, click on the + button, hover over ‘Add a tool’, then select the ‘Get wo products and inventory’ agent flow. The action will load inside the topic. For the Power Automate input ‘WorkorderID’, click on the three dots to add a powerFX formula and enter: Global.PA__Copilot_Model_PageContext.pageContext.id.guid This will add the GUID from the work order page the user is visiting in D365 Field Service. Since we want copilot to automatically sum up the total number of inventory products regardless of which warehouse they are in, I will use a prompt to do this. Click on the + button and hover over ‘Add a tool’ and select ‘New prompt’. We will configure the text that will be used for this prompt later. In the instructions section enter:
Start by letting the user know that these are the products that we have in stock for this work order. Don’t show any items that are less than 0. Summarize the following inventory data by product name and quantity. Give the results in a list for better readability. Use as Product and QTY as headers for each line and make them bold, for example:
**Product:** HVAC Unit
**Quantity:** 5
**Product:** Air Filter Replacement
**Quantity:** 0
Don’t add an explanation on how you got to the totals. Provide your data table here: _your data_<TEXT INPUT>
The <TEXT INPUT>in the prompt above needs to be replaced with a text input field. You can add the (dynamic) text input by clicking the ‘+add content’ button or by typing in / and selecting the text input. The data in the text input will be the results of the power automate flow that was created earlier. Once the text input has been added the prompt can be tested by clicking on the text input and entering some sample data. Click the ‘Save’ button to save the prompt This will close the prompt window and takes us back to the topic.

The prompt has been added below the power automate/agent flow node. In the ‘Text input’ of the prompt, we need to enter the results output from the agent flow from the previous node. Click on the three dots below the ‘Text Input’ in the prompt node, and selects the ‘Topic.Results’ variable from the custom variables list. The last step is to pass the summarized response from the prompt back to the user in a message. Below the prompt click on the + button and select ‘Send a message’. There are several variables available but we need to make sure we only get the summary of the text. Click on the {x} button and select the ‘PromptOutput.text’ variable. The last step is to save the topic and to publish the agent! Keep in mind that the functionality described in this article is just an example of how we can customize Copilot in Dynamics 365 Field Service. The ability to customize the agent opens a lot of doors when it comes to streamlining Field Service workflows! I hope you enjoyed this article! Be sure to check in again next week or subscribe here to never miss another post!



Comments are Closed