Accessing business-critical data from SAP traditionally involves navigating complex systems and user interfaces. But what if your team could simply ask for what they need — right from Microsoft Teams or Microsoft Copilot?
In this post, I’ll walk through how I built a Generative AI-powered Copilot agent using Microsoft Copilot Studio, integrated with SAP systems via SAP BTP Integration Suite and Azure API Management. The result is a seamless, conversational interface that allows users to retrieve sales orders, product information, supplier details, and customer records — all through natural language queries within Microsoft Teams. By combining Copilot Studio, Gen AI, SAP BTP, and Azure APIM, this solution bridges enterprise-grade backend systems with user-friendly front-end experiences, simplifying how business users interact with SAP data in the flow of work.
Step-by-Step Implementation: Here’s How We Did It
Connecting SAP backend OData services to external systems using SAP BTP API Management
For a detailed walkthrough on exposing SAP backend OData services securely and efficiently, refer to our blog “Connecting SAP Backend OData Services to External Systems Using SAP BTP API Management.”
It covers how to configure API providers, create API proxies, apply security policies such as application key verification, and manage API products within SAP Integration Suite. The blog follows SAP’s best practices and showcases a practical example using the SAP ES5 Gateway system. Ideal for developers and architects, it demonstrates how to enable scalable and secure API-based integration between SAP systems and external applications.
Configuring SSO for Seamless Authentication
This blog does not use Single Sign-On (SSO).
For a detailed, step-by-step implementation of SSO, refer to the blog “Up-level Your SAP OData APIs with Azure API Management“ by Martin Pankraz. His post covers both integration approaches: connecting directly to an S/4 HANA system using OAuth with Azure API Management and Microsoft Entra ID, as well as connecting to S/4HANA via SAP BTP API Management and Cloud Connector, also using OAuth and Microsoft Entra ID.
Microsoft’s blog on setting up SSO with SAP: Set up Microsoft Entra ID, Azure API Management, and SAP for SSO from SAP OData connector


Design an Agent Flow in Copilot Studio for Targeted Entity or Entityset actions
Agent flows provide a powerful mechanism for automating repetitive tasks and connecting your apps and services. They can be triggered manually, by other agents or automated events, or on a scheduled basis. Each action executed within an agent flow consumes Copilot Studio capacity.
Flows created in Power Automate can be used within Copilot Studio. PLease refer to Convert a Power Automate flow to an agent flow.
Agent Flows created for this use case

CategorySet: Retrieves product category data
We’ll start with a simple flow that fetches product categories.
This flow is designed to retrieve all available product categories, which will later be used to populate an option list when prompting the user for input during product searches in the Copilot agent.
It requires no input parameters and returns the complete list of categories from the SAP backend.

Every Agent flow in Copilot Studio begins with the trigger “When an agent calls the flow,” where you define the input parameters expected from the Copilot. It ends with “Respond to the agent,” where you configure the output parameters that will be returned to the Copilot agent.
In this scenario, the core action used within the flow is “SAP OData → Query OData Entities,” which retrieves the necessary data from the SAP backend.
🔧 For details on how to create and configure the OData connection used in this step, refer to the section below titled “Setup SAP OData Connector.”
Setup “SAP OData Connector”
If no OData connection exists in the current solution, you will be prompted to create one.
This use case utilizes Basic Authentication. Later this connection will be consumed by Copilot / Powerautomate flows to fetch data. To implement Single Sign-On, the option ‘Microsoft Entra ID via Azure API Management’ must be used.
Power Platform SAP ODAata Connector

When an SAP OData connection is created or used in Copilot Studio, a connection reference is automatically generated. This reference is reused whenever the same connector type is selected in a flow. If a new connection is created within the flow, a separate connection reference will be generated. When multiple connection references are available, you can choose a different one as needed.
If you want to use a connection that was previously created directly in Power Automate (under Data → Connections), you’ll need to manually create a corresponding connection reference. To do this, navigate to Solutions → Connection References → New Connection Reference and link it to your desired connection. Later you can switch the connection reference to the new one.
ProductSet
This flow is designed to be consumed by a Topic within the Copilot agent.
It leverages the ProductSet entity set to retrieve product data. The flow accepts a fully constructed OData filter query as input, which is used to search for products based on user criteria. It then returns the top 20 matching products from the SAP backend.

BusinessPartnerSet
This flow is invoked from Agent Tools and is used to retrieve a list of Business Partners from the SAP system.
It accepts the following input parameters:
- Business Partner Name (optional)
- Role – Customer or Supplier (mandatory)
- City (optional)
- Post Code (optional)
Based on these inputs, the flow dynamically constructs an OData filter condition, which is used to query the SAP backend. It then returns a filtered list of Customers or Suppliers matching the criteria.

SalesOrderDetail
This flow is utilized by Copilot Agent Tools to retrieve detailed sales order information.
It accepts a Sales Order Number as input and processes the response by separating it into Sales Order Header and Line Items. The Compose function is used to extract and reconstruct the header data, allowing for greater flexibility and control over how the output is formatted. This approach enables the tools to present header and line item data in distinct, tailored formats for an improved user experience.

SalesOrderSet
This flow is triggered by Agent Tools and is designed to retrieve sales order data based on optional user input.
It accepts the following parameters: Customer Name, Lifecycle Status, Billing Status, and Delivery Status – all of which are optional. The flow converts the provided status descriptions into their corresponding system codes, dynamically constructs an OData filter condition, and queries the SAP backend accordingly.
The resulting sales order data is then formatted into an HTML table, which is returned to the flow for display or further processing.

Build and Set Up a Copilot Agent with the Required Tools
🤖 Agent Spotlight: SAI (Sales AI)
SAI (Sales AI) is a purpose-built Copilot agent designed to streamline access to key business data across sales, procurement, and customer service functions. It enables users to retrieve up-to-date information from backend systems such as ERP or CRM platforms through simple, natural language queries.
This agent enhances productivity by automating common tasks like checking sales orders, reviewing product availability, or retrieving supplier and customer information—without the need for manual system navigation.
🔍 What SAI Can Do
Customer Info Access: Fetch customer records, contact details, and order history.
Sales Order Insights: Retrieve order status, header details, and line items using filters such as order number, billing status, or lifecycle stage.
Product Search: List products by category, price range, or stock availability.
Supplier Lookup: Get supplier names, contact details, and associated product lines.
⚙️ How It Works
SAI leverages dynamic topics and agent flows built in Copilot Studio to interact with backend SAP systems using OData APIs. It supports flexible filtering and returns formatted data—such as HTML tables—to provide clear, actionable responses.
Tool or Topic?
In this use case, both a Topic and a Tool are utilized within the Copilot agent.
| Item | Purpose | Where It’s Used |
|---|---|---|
| Topic | Drives the conversation (what the user wants to do) | Covers user intents and flows |
| Tool/Action | Does the work (like calling a system or sending data) | Used inside a topic |
Create a new topic when you want to:
- Handle a different user intent or a distinct conversation path.
- Answer a new type of user question.
- Guide users through a new process or workflow.
- Separate content areas for maintainability.
Create a tool or action when:
- The topic needs to perform a backend task, like calling an API, database, or system.
- You need to fetch or submit data dynamically.
- You want to automate something (e.g., send an email, log a ticket, update SharePoint, etc.).
Summary Cheat Sheet
| Use Case | Create Topic | Create Tool/Action |
|---|---|---|
| New question or task from user | ✅ | ❌ |
| Reusing same backend logic elsewhere | ❌ | ✅ |
| Connecting to SharePoint/API/Dataverse | ❌ | ✅ |
| User conversation branching | ✅ | ❌ |
| Getting data dynamically | ❌ | ✅ |
Tools (Agent)

Business Partner Set
The Tool leverages the BusinessPartnerSet flow, passing input values dynamically determined by Generative AI.
The flow is executed with this input, and the resulting output—typically a list of relevant business partners—is formatted and presented to the user using Generative AI for a more natural and contextual response.
In this use case, all input fields are dynamically populated using Generative AI and Copilot entities based on the user’s natural language prompts. Here’s how each input is processed:
- BP Role:
Interpreted from the user prompt and dynamically set to either"Customer"or"Supplier"using AI-based intent recognition.
(Refer to the code view for mapping logic.) - BP Name:
Extracted directly from the user input using AI-driven entity recognition and assigned as the business partner name.
(See code view for reference.) - City:
Tagged to the predefined Copilot entity:Cityand automatically filled by the AI based on the location mentioned in the user query. - Postal Code:
Tagged to the predefined Copilot entity:Zip Codeand populated dynamically when the user specifies a postal code in their request.
Code View
kind: TaskDialog
response:
activity:
mode: Generated
inputs:
- kind: AutomaticTaskInput
propertyName: text
description: |-
This field accepts two possible values: "Customer" or "Supplier".
Users can provide natural language prompts such as:
"Get me the list of suppliers"
"Show vendor details"
"Get me a list of customers"
"Find a supplier named SAP"
"List customers in a specific postcode"
- kind: AutomaticTaskInput
propertyName: text_2
entity: CityPrebuiltEntity
shouldPromptUser: false
- kind: AutomaticTaskInput
propertyName: text_3
description: This field take vaue as postcode
entity: ZipCodePrebuiltEntity
shouldPromptUser: false
- kind: AutomaticTaskInput
propertyName: text_1
description: |-
This field captures the name of the business partner - either a supplier or a customer.
Users can input natural language queries such as:
"Find a supplier with the name [Business Partner Name]"
"Get the customer whose name is [Business Partner Name]"
"Can you show me all customers named [Business Partner Name]?"
shouldPromptUser: false
outputs:
- propertyName: businesspartners
action:
kind: InvokeFlowTaskAction
flowId: cc1f4c78-474e-f011-877b-002248e17998
outputMode: AllProduct Set
The ProductSet Tool utilizes the agent flow named ProductSet, and is not called directly by the Copilot agent. Instead, it is invoked via the “Get Products” Topic.
The Topic interprets the user’s natural language input and extracts product filter criteria, such as category, or price. These values are combined as SAP OData filter string and assigned to a global variable, which is then passed to the ProductSet flow at runtime.
Completion action is marked as do not respond and the output returned by the flow is formatted into a structured table by Generative AI, using the defined output description.
(Refer to the code view for formatting details.)
Code View
kind: TaskDialog
inputs:
- kind: ManualTaskInput
propertyName: text
value: =Global.filterConditionProductSet
outputs:
- propertyName: products
description: Display as table
action:
kind: InvokeFlowTaskAction
flowId: 5633ffbf-a900-55c6-d2ed-96d25f088e21
outputMode: All
triggerCondition: falseSales Order Detail
The Sales Order Detail Copilot Tool enables users to quickly retrieve complete information about a specific sales order by providing a 10-digit Sales Order Number (e.g., 0500000011). It’s designed to support IT and customer service teams with fast, natural-language access to real-time backend data.
The Copilot automatically interprets the Sales Order Number from the prompt using Generative AI.
Users can enter prompts like:
- “Show me the details for sales order 0500000011”
- “What’s the status of order 0500000011?”
- “I’d like to see the item details for order 0500000011”
The flow completion is handled by sending a blank message, allowing the output to display cleanly without additional text. Sales Order Summary is presented in a clear name–value format, showcasing key details such as order status, customer information, and order date. Line Items are rendered in a structured table format, including fields like item number, product ID, gross amount, quantity, and notes.
Code View
kind: TaskDialog
response:
activity:
mode: Strict
inputs:
- kind: AutomaticTaskInput
propertyName: text
description: |-
This field can take 10 digits as input. A sample of sales order ID is 0500000002.
The user can provide a prompt such as
"Show me the details for sales order 0500000011.",
"What’s the status of order 0500000011?",
"Can you give me an update on 0500000011?",
"I’d like to see the items info for order 0500000011.",
"Please pull up order 0500000011 for me.",
"Tell me everything about sales order 0500000011.",
"I want to check the delivery status for 0500000011.",
"What’s happening with sales order number 0500000011?",
"Can I get the billing status for 0500000011?",
"Is order 0500000011 processed yet?"
modelDisplayName: Get Sales Order Detail
modelDescription: |-
This Copilot allows IT and support teams to quickly retrieve comprehensive sales order details for any customer. By providing either a Sales Order ID or customer information (such as name, email, or order date), users can access real-time data including order status, itemized line details, quantities, prices, delivery timelines, and tracking info. Designed for seamless internal use, the tool simplifies order investigations and enhances customer support efficiency."
The user can provide a prompt such as
"Show me the details for sales order.",
"What’s the status of order?",
"I’d like to see the items info for order.",
"Please pull up order for me.",
"Show me the details for sales order 0500000011.",
"What’s the status of order 0500000011?",
"Can you give me an update on 0500000011?",
"I’d like to see the items info for order 0500000011.",
"Please pull up order 0500000011 for me.",
"Tell me everything about sales order 0500000011.",
"I want to check the delivery status for 0500000011."
outputs:
- propertyName: salesorderheader
name: Present salesOrderHeader as Name & Value Pair
description: Present salesOrderHeader as Name & Value Pair
- propertyName: salesorderlineitems
name: "Display salesOrderLineItems as Table "
description: Display salesOrderLineItems as Table including Item Position, Product ID, Gross Amount, Quantity & Note
action:
kind: InvokeFlowTaskAction
flowId: 10c95ff8-134c-f011-877a-002248e17998
outputMode: AllSales Order Set
The Get Sales Order List Copilot tool enables users to retrieve a filtered list of sales orders based on a combination of optional criteria, all extracted dynamically from natural language prompts using Generative AI.
Users can specify one or more of the following input through conversational prompts:
- Customer Name – e.g., “Show all orders for Acme Corp”
- Delivery Status – Delivered or Undelivered
- Billing Status – Paid or Unpaid
- Lifecycle Status – New, In Progress, or Closed
These fields are automatically filled based on user input without prompting for individual values.
Example Use Cases:
- “List all undelivered sales orders for Acme Inc.”
- “Show paid and closed orders for GlobalTech.”
- “What new sales orders haven’t been processed yet?”
- “Get all unpaid and undelivered orders.”
On completion response is marked as “Write response using Generative AI”. It generates a filtered list of sales orders and present it as a table, including key details such as Sales Order ID, Customer Name, Gross Amount, Net Amount, Lifecycle Status
Code View
kind: TaskDialog
response:
activity:
mode: Generated
inputs:
- kind: AutomaticTaskInput
propertyName: text_1
description: |-
This field is name of customer. User can provide customer name as part of prompts.
Few such prompts are "Show all sales orders for customer [Customer Name],
"Retrieve the sales orders placed by [Customer Name]",
"Get a list of all orders for the customer named [Customer Name]",
"Display customer order history for [Customer Name]",
"List every sales order associated with [Customer Name]",
"Can you show me what [Customer Name] has ordered so far?",
"What orders has [Customer Name] placed?",
"Tell me all the orders linked to [Customer Name]",
"Has [Customer Name] placed any recent sales orders?",
"Pull up the order history for [Customer Name], please"
shouldPromptUser: false
inputSettings:
repeatCount: 0
defaultValue: =Blank()
- kind: AutomaticTaskInput
propertyName: text_2
description: |-
This field can take two input Delivered or Undelivered.
User promps can be such as
"Show all delivered sales orders",
"Retrieve the list of completed/delivered sales orders",
"Display sales orders marked as delivered",
"List orders that have already been delivered to customers",
"Get a report of fulfilled sales orders",
"Show all pending or undelivered sales orders",
"Retrieve the list of sales orders yet to be delivered",
"Display sales orders with delivery status 'pending' or 'not delivered'",
"List open orders awaiting delivery",
"Get the report of unfulfilled sales orders",
"Which sales orders have been successfully delivered?"
"Can I see a list of all orders that have been delivered?"
"What sales have been fulfilled already?",
"Give me a summary of delivered customer orders",
"Which orders are marked as delivered?",
"Which sales orders haven’t been delivered yet?",
"Are there any customer orders still pending delivery?",
"Show me what orders are still waiting to be delivered"
shouldPromptUser: false
inputSettings:
repeatCount: 0
defaultValue: =Blank()
- kind: AutomaticTaskInput
propertyName: text
description: |-
This field can take two values "Paid" and "Unpaid".
User prompts can be such as
"Show all fully paid sales orders.",
"List sales orders with payment status marked as 'Paid'.",
"Retrieve all completed payments for sales orders.",
"Display paid invoices or sales orders.",
"Get a report of sales orders where payment has been received.",
"Which sales orders have already been paid for?",
"Can I see all orders that have been fully paid?",
"Show me paid orders in the system.",
"Give me a list of completed and paid sales transactions.",
"What sales orders have settled payments?",
"Show all unpaid or outstanding sales orders.",
"List sales orders with payment status 'Unpaid' or 'Pending'.",
"Retrieve sales orders awaiting payment.",
"Display invoices that have not been paid yet.",
"Get a report of open sales orders with pending payments.",
"Which sales orders haven’t been paid yet?",
"Are there any open orders with pending payments?",
"Can you show me all outstanding customer invoices?"
shouldPromptUser: false
inputSettings:
repeatCount: 0
defaultValue: =Blank()
- kind: AutomaticTaskInput
propertyName: text_3
description: |-
This field can take vlaue "New", "In Progress", "Closed".
User can give prompt such as
"Which sales orders haven’t been started yet?",
"List all orders sitting in 'new' status",
"Can you show me orders that are just entered?",
"Are there any sales orders that haven’t entered the processing stage?",
"Show sales orders that have been created but not yet started.",
"List all newly created sales orders.",
"Retrieve sales orders with status 'New'.",
"List all active or in-progress sales orders.",
"Show sales orders currently being processed.",
"Retrieve orders with status 'In Progress' or 'Processing'.",
"Display orders that are not yet completed or closed.",
"Get all sales orders currently under fulfillment.",
"Display today's newly added sales orders.",
"List all closed or completed sales orders.",
"Show sales orders with status 'Closed'.",
"Get a report of closed customer orders."
shouldPromptUser: false
inputSettings:
repeatCount: 0
defaultValue: =Blank()
modelDisplayName: Get Sales Order List
modelDescription: |-
This Copilot tool helps users quickly retrieve a filtered list of sales orders based on multiple input criteria. Users can specify one or more of the following parameters to refine their search:
Customer Name: The name of the customer for whom the sales orders are to be retrieved.
Delivery Status: Whether the sales orders are Delivered or Undelivered.
Billing Status: Whether the sales orders are Paid or Unpaid.
Lifecycle Status: The current stage of the sales order — New (not started), In Progress, or Closed.
Example Use Cases:
"Show all undelivered sales orders for Acme Corp."
"List paid and closed orders for GlobalTech."
"Get new sales orders that haven't been processed yet."
"Show undelivered and unpaid orders for Acme Inc."
"List all paid, delivered sales orders."
The tool returns a filtered list of sales orders and present the results as HTML table including following information Sales Order ID, Customer Name, Gross amount, Net Amount, Life Cycle Status.
outputs:
- propertyName: salesorders
- propertyName: salesorderscount
action:
kind: InvokeFlowTaskAction
flowId: b0a68d28-754a-f011-877b-002248e17998
outputMode: AllTopic
Get Products
This Copilot Topic enables users to search for products by specifying a product category using natural language prompts. It supports a wide range of product categories such as Notebooks, Headsets, Tablets, Smartphones, and more.
- The topic listens for prompts like:
- “Show me Notebooks”
- “Do you have any Smartphones?”
- “List all Headsets under $1000”
- If the product category is not detected in the prompt, the system triggers a CategorySet flow to fetch available categories and presents them as selectable options using a dynamic dropdown list.
- Once the user selects or provides a valid category, the dialog:
- Builds an OData filter condition
- Stores it in a global variable for use by downstream tools or flows
- After the filter is prepared, it calls the ProductSet agent flow to retrieve matching products.
- The results are returned and formatted as a table, displaying key product details.
Code View
kind: AdaptiveDialog
inputs:
- kind: AutomaticTaskInput
propertyName: ProductCategory
description: |-
This is a product category. The user provides this while searching for products. User prompts can be such as "Show me Notebooks", "List all Headsets", "Could you please show me some Tablets?", "Do you have any Smartphones?".
Different known product categories are
Accessories,
Camcorders,
Computer System Accessories,
Flat Screen Monitors,
Flat Screen TVs,
Graphic Cards,
Headsets,
Ink Jet Printers,
Keyboards,
Laser Printers,
MP3 Players,
Mice,
Mousepads,
Multifunction Printers,
Notebooks,
PCs,
PDAs & Organizers,
Portable Players,
Projectors,
Scanners,
Servers,
Smartphones,
Software,
Speakers,
Tablets,
Telecommunications
entity: StringPrebuiltEntity
shouldPromptUser: false
modelDescription: |-
"Show me products in [category]",
"I want to see [category] items",
"List [category] under $1000",
"Do you have [category]?",
"Find me [category] by price",
"What kind of [category] do you offer?",
"Get product below 2000$",
"List of products between 1000$ and 3000$"
beginDialog:
kind: OnRecognizedIntent
id: main
intent: {}
actions:
- kind: SetVariable
id: setVariable_c46i94
variable: Topic.selectedProductCategory
value: =Blank()
- kind: ConditionGroup
id: conditionGroup_7weYWf
conditions:
- id: conditionItem_qcifPq
condition: =IsBlank(Topic.ProductCategory)
displayName: Product Category Is BLANK
actions:
- kind: InvokeFlowAction
id: invokeFlowAction_RQcLEr
displayName: Get Categories
input: {}
output:
binding:
productcategories: Topic.productCategories
flowId: ec35827d-b2a5-d46d-304d-c479952f957d
- kind: ParseValue
id: 8Am2dk
displayName: Parse JSON String for Categories
variable: Topic.Categories
valueType:
kind: Record
properties:
data:
type:
kind: Table
properties:
Category: String
value: =Topic.productCategories
- kind: SetVariable
id: setVariable_yYopgx
displayName: Set Categories List Variable
variable: Topic.CategoryList
value: =Distinct(Topic.Categories.data,Category)
- kind: Question
id: question_ku86Nw
displayName: Ask product category
interruptionPolicy:
allowInterruption: true
alwaysPrompt: true
variable: Topic.selectedProductCategory
prompt: To help you better, please select a product category.
entity:
kind: DynamicClosedListEntity
sensitivityLevel: None
items: =Topic.CategoryList
elseActions:
- kind: SetVariable
id: setVariable_gSxOqP
displayName: Set selectedProductCategory
variable: Topic.selectedProductCategory
value: =Topic.ProductCategory
- kind: SetVariable
id: setVariable_s133Kx
displayName: Set filterCondition
variable: Global.filterConditionProductSet
value: ="Category eq '" & Topic.selectedProductCategory &"'"
- kind: SetVariable
id: UYKwjm
displayName: Clear ProductCategory for Next Iterations
variable: Topic.ProductCategory
value: =Blank()
- kind: SetVariable
id: lea1RM
displayName: Clear selectedProductCategory for Next Iterations
variable: Topic.selectedProductCategory
value: =Blank()
- kind: BeginDialog
id: xVUcbh
input: {}
dialog: Default_saiSalesAi.action.ProductSet
output:
binding:
products: Topic.products
inputType:
properties:
ProductCategory:
displayName: ProductCategory
description: |-
This is a product category. The user provides this while searching for products. User prompts can be such as "Show me Notebooks", "List all Headsets", "Could you please show me some Tablets?", "Do you have any Smartphones?".
Different known product categories are
Accessories,
Camcorders,
Computer System Accessories,
Flat Screen Monitors,
Flat Screen TVs,
Graphic Cards,
Headsets,
Ink Jet Printers,
Keyboards,
Laser Printers,
MP3 Players,
Mice,
Mousepads,
Multifunction Printers,
Notebooks,
PCs,
PDAs & Organizers,
Portable Players,
Projectors,
Scanners,
Servers,
Smartphones,
Software,
Speakers,
Tablets,
Telecommunications
type: String
outputType:
properties:
products:
displayName: products
description: Display results as table
type: StringPublish the agent to Teams and Copilot Channel
Navigate to the “Channels” section and add the Copilot to Microsoft Channels. Once added, use the available options to open the agent in Microsoft 365 or Teams. To allow others to access the agent, you’ll need to share it with specific individuals or your organization.
Connect and configure an agent for Teams and Microsoft 365



See the Copilot Agent in Action
🎥 Watch the Demo:
See the Sales AI Copilot agent in action—integrating SAP backend OData services via SAP BTP and Azure APIM, delivered through Microsoft Teams and M365 Copilot.
Git Hub Source
Explore the complete source code, flow definitions, and configuration files for the Sales AI Copilot agent demo.
👉 GitHub – aditheos/SalesAI