Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
[This article is prerelease documentation and is subject to change.]
When the data you want to use with your connector is structured with tables or lists, the enhanced connector protocol helps you deliver high-performance, enterprise grade connector more easily. Enhanced connectors serve as powerful knowledge source for agents and allow you to easily build canvas apps in Power Apps and compose logic using Power Automate.
Important
- This is a preview feature.
- Preview features aren’t meant for production use and might have restricted functionality. These features are subject to supplemental terms of use, and are available before an official release so that customers can get early access and provide feedback.
Action and enhanced connectors
Connectors created using OpenAPI (swagger) endpoints are considered action connectors. OpenAPI provides a standardized format to describe HTTP APIs of any size. It allows you to explicitly define endpoints (paths), supported HTTP methods, and request/response schemas. Each operation (a path and method combination) represents a specific callable action of the API, making it well-suited for APIs that follow RESTful principles.
For structured data sources, the fine-grained control OpenAPI provides becomes burdensome because the tabular data sources aren't usually defined by an OpenAPI endpoint. The OpenAPI definition isn't bound to metadata that describes the structured data, so it doesn't adapt to changes that add new tables, columns, or relationships to a data source. The connector generated using the static OpenAPI definition can't represent these changes until it's regenerated.
Enhanced connectors require an endpoint that provides information that describes dynamic set of tables, schemas, and capabilities. This article describes a protocol you can implement to create a ASP.NET Core Web API based on a protocol similar to OData rather than OpenAPI. This protocol is designed for tabular data and uses OData capabilities to manage filtering, sorting, paging, and relationships that OpenAPI doesn't provide.
Note
The endpoint described in this article has some similarities to OData, but doesn't implement the actual OData protocol.
Structured data
What do we mean by structured data? Data could be structured using actual database tables, but it could also use different terminology like 'site' rather than 'database' and 'list' rather than 'table'. The key point is that the data is structured into a resource hierarchy like this:
- A dataset exposes a collection of tables
- A table has rows and columns that contain data
- An item represents a row of a table
Connectors using this pattern
The following are some of the enhanced connectors that use this pattern:
How it works
The enhanced connector protocol has three main parts:
- Capabilities endpoints: Describe the datasets, tables, and columns the connector can access and what they can do.
- Transpiler: Converts an OData style
GET
requests to retrieve data for the datasource. These requests use OData query options like$filter
,$select
,$orderby
,$sort
, and$top
- CUD operation implementation: Describes how to perform Create, Update, and Delete operations on resources for the datasource
At a high level, you need to implement the following process:
- Create a ASP.NET Core Web API that implements the enhanced connector protocol.
- Deploy the ASP.NET Core Web API project to the hosting environment of your choice.
- Create the custom connector using the paconn command line tool.
- Configure authentication.
- Share and test the connector.
Note
The Power Fx enhanced connector sample is a Visual Studio solution published on GitHub. You can clone the repository and use the provided ASP.NET Core Web API project as a starting point for your endpoint.
Capabilities endpoints
The enhanced connector protocol depends on implementing five endpoints that describe the capabilities of your service. The following table summarizes these endpoints and includes links to the sections of this document that explain how to implement them.
Route | How |
---|---|
GET $metadata.json/datasets |
Return list of datasets |
GET /datasets |
Return dataset names |
GET /datasets/{dataset}/tables |
Return table names |
GET $metadata.json/datasets/{dataset}/tables/{tableName} |
Return the capabilities of a table |
GET /datasets/{dataset}/tables/{tableName}/items |
Retrieve the table column values |
ITableProvider interface
The following ITableProviderFactory
and ITableProvider
interfaces provide the expected methods to implement the enhanced connector protocol capabilities endpoints. Add the following code within the Services
folder of your ASP.NET Core Web API project.
// Helper to get a provider for a given auth token.
public interface ITableProviderFactory
{
ITableProvider Get(IReadOnlyDictionary<string, string> settings);
}
/// <summary>
/// Datasource Provider. Host implements this to provide the RecordTypes for a specific source.
/// </summary>
public interface ITableProvider
{
// Return list of datasets (logical name, display name)
public Task<DatasetResponse.Item[]> GetDatasetsAsync(
CancellationToken cancellationToken = default
);
// Provider list of the tables.
public Task<GetTablesResponse> GetTablesAsync(
string dataset,
CancellationToken cancellationToken = default);
public Task<RecordType> GetTableAsync(
string dataset,
string tableName,
CancellationToken cancellationToken = default);
public Task<TableValue> GetTableValueAsync(
string dataset,
string tableName,
CancellationToken cancellationToken = default);
}
Implement your ASP.NET Core Web API with a separation between the front-end (REST API) and the back-end (data source provider). The back-end is abstracted via the ITableProvider
interface, which you can implement to connect to any tabular data source.
Classes used
In the ASP.NET Core Web API application you create, add the Microsoft.PowerFx.Connectors and Microsoft.PowerFx.Core NuGet packages so that you have access to types that are used in ITableProvider
, such as RecordType and TableValue
Some other types you need aren't currently included in the PowerFx NuGet packages. Find them within the Power Fx enhanced connector sample power-fx-enhanced-connector/CdpHelpers/Protocol folder using the same Microsoft.PowerFx.Connectors
namespace. See the table describing these types.
Return list of datasets
This endpoint in the service provides a lightweight, JSON‐formatted catalog of all top‐level data containers—what you might think of in SQL as running SELECT name FROM sys.databases
. By fetching only the names and access URLs of each dataset, clients can dynamically discover which logical units (databases) the service exposes without hard‐coding any identifiers. This design not only drives metadata‐first UI or code‐generation workflows—letting users pick a dataset and then drill into its tables or views—but also respects authorization rules by only returning those datasets the caller is permitted to see.
Implement this route in your controller to provide data about the datasets available:
GET $metadata.json/datasets
Note
This route isn't included in the ITableProvider
interface.
Return a DatasetMetadata instance that has the following properties:
Name | Type | Description |
---|---|---|
DatasetFormat | String | Describes the format of Dataset string. For Example for SQL it can be "{server},{database}" |
IsDoubleEncoding | Boolean | Indicates whether the metadata blob is encoded twice (for example: a JSON payload wrapped as a JSON-encoded string), requiring two decoding passes to retrieve the raw metadata. |
Parameters | IReadOnlyCollection<MetadataParameter> | See MetadataParameter |
Tabular | MetadataTabular | See MetadataTabular |
Note
In the Power Fx enhanced connector sample, CdpSampleWebApi/Controllers/CdpController.cs DatasetMetadata
is renamed to DatasetMetadataResponse
. This name would be consistent with names used in the ITableProvider
interface if it was included.
MetadataParameter
This metadata describes how client applications should collect, validate, and encode each query parameter when calling your dataset endpoint. By setting these properties, you ensure:
- User interfaces can render clear labels and tooltips for each parameter.
- Required fields are enforced at design time, preventing runtime errors.
- Input values are validated against the declared type (string, integer, boolean, and so on).
- URL encoding is applied correctly so special characters don't break the request.
Set the DatasetMetadata.Parameters property with a MetadataParameter instance that contains these properties:
Name | Type | Description |
---|---|---|
Description | String | A human-readable explanation of what this parameter represents and how it affects the dataset request. For example: this value is server name or database name in SQL context. |
Name | String | The exact parameter name clients must include in the query string or request body |
Required | String | Indicates whether the client must supply this parameter. If true, omitting the parameter results in an error; if false, a default or fallback behavior might apply. |
Type | String | The data type of the parameter value, such as string , integer , boolean , which tells clients how to validate and convert the input before issuing the request. |
UrlEncoding | String | Specifies whether the parameter value should be URL‐encoded once (single ) or twice (double ). |
XMsDynamicValues | MetadataDynamicValues | See MetadataDynamicValues |
XMsSummary | String | A concise summary used in UI tooltips or documentation to help users understand the purpose of this parameter at a glance. |
MetadataDynamicValues
This metadata tells client applications how to retrieve and render a live list of valid options for a given parameter—enabling dropdowns, pickers, or search-as-you-type experiences in your UI. By setting these properties, you ensure:
- Clients know which endpoint or path to call to fetch the current list of values
- The response payload is parsed correctly to extract both the underlying values and their display titles
- Parameter inputs stay in sync with your service's available options, reducing errors and improving user experience
When setting the DatasetMetadata.Parameters collection, set the XMsDynamicValues
property to a MetadataDynamicValues instance. This class has the following string properties:
Name | Description |
---|---|
Path | The relative URL or OData path clients call to fetch the list of dynamic values (For example: /datasets/{dataset name}/tables ). |
ValueCollection | The JSON property name in the response payload that contains the array of items (For example: value or items ). |
ValuePath | The JSON path (relative to each item) to extract the actual parameter value (For example: id or name.value ). |
ValueTitle | The JSON path (relative to each item) to extract the user-facing display text for each option (For example: displayName or title ). |
MetadataTabular
This metadata tells client code how to present and access your service's tabular data. By setting these properties, you ensure that user interfaces display terminology and generate URLs correctly for your specific backend. For example, a SQL-based service would use Table/Tables
, whereas a SharePoint connector would use List/Lists
; and proper URL-encoding settings guarantee that resource paths are built correctly.
Set the DatasetMetadata.Tabular property with a MetadataTabular instance that contains these string properties:
Name | Description |
---|---|
DisplayName | Display name for Dataset. For example Database/WebSite . |
Source | It can be mru or singleton . If your service only ever has one logical data container, then /datasets effectively becomes a singleton endpoint. In a multi‐dataset scenario, you might still not want to overwhelm clients with every possible dataset at once. Instead, you track usage history and have /datasets return only the top N datasets ordered by last access time (or last modification). This way users see the Most Recently Used (mru) up front, and can later drill into a full list endpoint or enable paging if they need more. |
TableDisplayName | The display name of the table like datasource, like 'Table' or 'List'. |
TablePluralName | The plural name of the table like datasource, like 'Tables' or 'Lists'. |
UrlEncoding | Can be single or double . |
Return dataset names
Implement this route in your controller to provide collection of names for each dataset:
GET /datasets
Implement the ITableProvider.GetDatasetsAsync
method to return a DatasetResponse instance that has a value
property with an array of Item instances.
Each Item
has string Name
and DisplayName
properties.
Note
Connectors aren't required to provide multiple datasets. When there's only one dataset, the convention is to set both the Name
and DisplayName
properties of the single item to default
.
Return table names
Implement this route in your controller to provide collection of names for tables in a dataset:
GET /datasets/{dataset}/tables
Implement the ITableProvider.GetTablesAsync
method to return a GetTablesResponse instance. This class has a Value
property that returns a List<RawTablePoco>
.
RawTablePoco has the two string properties: Name
and DisplayName
.
Note
'POCO' stands for 'Plain Old CLR Objects' and is used as a way to format response data in ASP.NET Core Web API.
Return the capabilities of a table
Implement this route in your controller to return data about the capabilities of tables in a dataset:
GET $metadata.json/datasets/{dataset}/tables/{tableName}
Implement the ITableProvider.GetTableAsync
method to return a RecordType then convert that value into a GetTableResponse. The RecordType.ToTableResponse method provides an example showing how. You should add the CdpHelpers/RecordTypeExtensions.cs file to your project to use it.
GetTableResponse
has the following properties:
Name | Type | Description |
---|---|---|
capabilities |
CapabilitiesPoco | Gets or sets the table capabilities, such as filtering and sorting support. See Describe table capabilities |
name |
String | Gets or sets the name of the table. |
permissions |
String | Gets or sets the permissions for the table (for example: "read-write"). |
schema |
TableSchemaPoco | Gets or sets the schema of the table. See Describe table column capabilities |
Describe table capabilities
The GetTableResponse.capabilities
property describes the capabilities of a table using the CapabilitiesPoco class.
The CapabilitiesPoco
class has the following properties to describe the capabilities of a table.
Name | Type | Description |
---|---|---|
filterFunctionSupport |
string[] |
Gets or sets the supported filter functions (for example: eq , and , or ). |
filterRestrictions |
Filter |
The Filter class has a boolean filterable property to describe whether or not the table supports any filtering at all. When filterable is true, the nonFilterableProperties property contains an array of table column names that aren't filterable. |
isOnlyServerPagable |
bool |
Gets or sets a value indicating whether the table is only server pageable. |
odataVersion |
int |
Gets or sets the OData version (for example: 3). |
serverPagingOptions |
string[] |
Gets or sets the server paging options (for example: top , skiptoken ). |
sortRestrictions |
Sort |
The Sort class has a boolean sortable property to describe whether or not the table supports any sorting at all. When sortable is true, the unsortableProperties property contains an array of table column names that aren't sortable. |
Describe table column capabilities
The GetTableResponse.schema
property describes the capabilities of table columns using the TableSchemaPoco class.
Note
This section describes a set of classes that have simple and complex properties. The properties with complex type create a hierarchy that describes the capabilities of the columns for a table.
TableSchemaPoco.items
Items.properties
ColumnInfo.capabilities
ColumnCapabilitiesPoco
The TableSchemaPoco
class has the following properties to describe the capabilities of a table.
Name | Type | Description |
---|---|---|
type |
string | Gets or sets the type of the schema (default is "array"). |
items |
Items | Gets or sets the items definition, which describes the columns of the table. |
The Items class has the following properties:
Name | Type | Description |
---|---|---|
type |
string | Gets or sets the type of the items (default is "object"). |
properties |
Dictionary<string,ColumnInfo> | Gets or sets the dictionary of column properties, keyed by column name. |
The ColumnInfo class has the following properties:
Name | Type | Description |
---|---|---|
title |
string | Gets or sets the title of the column. |
description |
string | Gets or sets the description of the column. |
type |
string | Gets or sets the type of the column (for example: integer , string ). |
sort |
string | Gets or sets the sort capabilities for the column (for example: asc , desc ). |
capabilities |
ColumnCapabilitiesPoco | Gets or sets the filter capabilities for the column. |
The ColumnCapabilitiesPoco class has the following properties:
Name | Type | Description |
---|---|---|
filterFunctions |
string[] | Gets or sets the supported filter functions for the column (for example: eq , and , or ). |
Retrieve the table column values
Implement this route in your controller to return table column values:
GET /datasets/{dataset}/tables/{tableName}/items
Implement the ITableProvider.GetTableValueAsync
method to return a TableValue then convert that value into a GetItemsResponse
GetItemsResponse
has only a single value
property that is a List<Dictionary<string, object>>
containing the keys and values for the respective table properties.
Transpiler
The ASP.NET Core Web API you create needs to allow users to specify optional OData query string parameters when this route is used:
GET /datasets/{dataset}/tables/{tableName}/items
The service needs to support the capabilities advertised for the table and the columns.
The CdpSampleWebApi/Services/ODataQueryModel.cs is a lightweight model-binding container for a subset of OData system query options. Its purpose:
Model binding: ASP.NET automatically populates its properties from query string parameters named
$select
,$filter
,$top
, and$orderby
thanks to the[FromQuery(Name="...")]
attributes. For example:/entities?$select=id,name&$top=10
.Raw capture only: It doesn't parse or validate OData expressions; it preserves the client-supplied text so downstream code (For example: a data layer or a relay to another API) can decide how to interpret, validate, or forward them.
Conditional packaging:
ToStrDict()
converts only the provided values (and$top
only if > 0) into a dictionary, useful for:- Reconstructing or forwarding the same OData query options to another service.
- Logging/auditing.
- Building a query string programmatically.
Naming/casing choices: top and orderby are lowercase to mirror the OData keywords exactly (style/analyzer warnings suppressed at file top). Select and Filter use PascalCase (mixed style), but the binding still works because of the explicit Name override.
Scope: It intentionally omits other OData options (
$skip
,$expand,
$count
,$search
, and others), keeping the surface minimal. Potential enhancements (if needed):- Make top nullable (int?) to distinguish between "absent" and "explicit 0".
- Add
$skip
,$expand
, or other query parameters as requirements grow. - Add validation or parsing (For example: approve fields in
$select
, expression parsing for$filter
). - Normalize property naming for consistency.
In short, ODataQueryModel
class is a simple pass-through carrier of selected OData query options, enabling safe, explicit, and testable access to them inside controller actions or services.
Create, update, and delete operation implementation
As you can see, the enhanced connector protocol outlines specific ways to retrieve information about the capabilities of your data sets and retrieve data. The next phase is to provide capabilities to add, change, or remove table rows.
Creating rows
Your service should use POST
HTTP method with the same resource path used to retrieve records. The payload is sent with the body of the request.
POST /datasets/{dataset}/tables/{tableName}/items
Updating and deleting rows
Updating or deleting rows requires some way to identify the record you want to change or remove. This depends on the nature of your data source, but you should use the same resource paths. To change a record, use PATCH
.
PATCH /datasets/{dataset}/tables/{tableName}/items/{primary key}
To delete a record, use DELETE
.
DELETE /datasets/{dataset}/tables/{tableName}/items/{primary key}
Limitations
The following are areas where common tabular data capabilities aren't enabled using the tabular data connection protocol.
Relationships
It isn't currently possible to model relationships between tables.
Data types are limited
Only types supported by Swagger 2.0 will work.
Next steps
Learn about the Enhanced connector sample.