Skip to content
Back to Blog
1 min read

Power Platform Custom Connectors: Extending Low-Code Capabilities

I wrote “Power Platform Custom Connectors: Extending Low-Code Capabilities” to share practical, production-minded guidance on this topic.

Custom Connector Overview

Custom connectors bridge the gap between your APIs and the Power Platform ecosystem, enabling:

  • Power Apps integration
  • Power Automate workflows
  • Logic Apps orchestration

Creating a Custom Connector

Define your connector using OpenAPI specification:

# connector-definition.yaml
swagger: "2.0"
info:
  title: Product Catalog API
  description: API for managing product catalog
  version: "1.0"
host: api.example.com
basePath: /v1
schemes:
  - https
consumes:
  - application/json
produces:
  - application/json
securityDefinitions:
  apiKey:
    type: apiKey
    in: header
    name: X-API-Key
security:
  - apiKey: []

paths:
  /products:
    get:
      summary: Get all products
      operationId: GetProducts
      parameters:
        - name: category
          in: query
          type: string
          description: Filter by category
        - name: minPrice
          in: query
          type: number
          description: Minimum price filter
        - name: maxPrice
          in: query
          type: number
          description: Maximum price filter
        - name: page
          in: query
          type: integer
          default: 1
        - name: pageSize
          in: query
          type: integer
          default: 20
      responses:
        200:
          description: List of products
          schema:
            $ref: "#/definitions/ProductListResponse"
    post:
      summary: Create a product
      operationId: CreateProduct
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/CreateProductRequest"
      responses:
        201:
          description: Product created
          schema:
            $ref: "#/definitions/Product"

  /products/{id}:
    get:
      summary: Get product by ID
      operationId: GetProduct
      parameters:
        - name: id
          in: path
          required: true
          type: string
      responses:
        200:
          description: Product details
          schema:
            $ref: "#/definitions/Product"
        404:
          description: Product not found
    put:
      summary: Update product
      operationId: UpdateProduct
      parameters:
        - name: id
          in: path
          required: true
          type: string
        - name: body
          in: body
          required: true
          schema:
            $ref: "#/definitions/UpdateProductRequest"
      responses:
        200:
          description: Product updated
          schema:
            $ref: "#/definitions/Product"
    delete:
      summary: Delete product
      operationId: DeleteProduct
      parameters:
        - name: id
          in: path
          required: true
          type: string
      responses:
        204:
          description: Product deleted

definitions:
  Product:
    type: object
    properties:
      id:
        type: string
      name:
        type: string
      description:
        type: string
      price:
        type: number
      category:
        type: string
      imageUrl:
        type: string
      inStock:
        type: boolean
      createdAt:
        type: string
        format: date-time

  ProductListResponse:
    type: object
    properties:
      products:
        type: array
        items:
          $ref: "#/definitions/Product"
      totalCount:
        type: integer
      page:
        type: integer
      pageSize:
        type: integer

  CreateProductRequest:
    type: object
    required:
      - name
      - price
      - category
    properties:
      name:
        type: string
      description:
        type: string
      price:
        type: number
      category:
        type: string
      imageUrl:
        type: string

  UpdateProductRequest:
    type: object
    properties:
      name:
        type: string
      description:
        type: string
      price:
        type: number
      category:
        type: string
      imageUrl:
        type: string
      inStock:
        type: boolean

Backend API Implementation

Create the API that the connector will call:

// Controllers/ProductsController.cs
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("v1/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpGet]
    public async Task<ActionResult<ProductListResponse>> GetProducts(
        [FromQuery] string? category,
        [FromQuery] decimal? minPrice,
        [FromQuery] decimal? maxPrice,
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20)
    {
        var filter = new ProductFilter
        {
            Category = category,
            MinPrice = minPrice,
            MaxPrice = maxPrice
        };

        var (products, totalCount) = await _productService.GetProductsAsync(filter, page, pageSize);

        return Ok(new ProductListResponse
        {
            Products = products,
            TotalCount = totalCount,
            Page = page,
            PageSize = pageSize
        });
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Product>> GetProduct(string id)
    {
        var product = await _productService.GetByIdAsync(id);

        if (product == null)
            return NotFound();

        return Ok(product);
    }

    [HttpPost]
    public async Task<ActionResult<Product>> CreateProduct([FromBody] CreateProductRequest request)
    {
        var product = await _productService.CreateAsync(request);
        return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
    }

    [HttpPut("{id}")]
    public async Task<ActionResult<Product>> UpdateProduct(
        string id,
        [FromBody] UpdateProductRequest request)
    {
        var product = await _productService.UpdateAsync(id, request);

        if (product == null)
            return NotFound();

        return Ok(product);
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(string id)
    {
        var deleted = await _productService.DeleteAsync(id);

        if (!deleted)
            return NotFound();

        return NoContent();
    }
}

Policy Templates for Connectors

Add custom policies for data transformation:

// Connector Policy Template
public class ConnectorPolicyTemplate
{
    public string TemplateId { get; set; }
    public string Title { get; set; }
    public List<PolicyParameter> Parameters { get; set; }
}

// Example: Add header policy
{
    "templateId": "setheader",
    "title": "Set API Key Header",
    "parameters": {
        "x-api-key": "@connectionParameters('apiKey')"
    }
}

// Example: Transform response
{
    "templateId": "routeRequestToEndpoint",
    "title": "Route to endpoint",
    "parameters": {
        "x-ms-apimTemplateParameter.newPath": "/v1/products"
    }
}

Power Automate Flow Example

Use the custom connector in a flow:

{
  "definition": {
    "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
    "contentVersion": "1.0.0.0",
    "triggers": {
      "When_a_new_email_arrives": {
        "type": "ApiConnectionWebhook",
        "inputs": {
          "host": {
            "connection": {
              "name": "@parameters('$connections')['office365']['connectionId']"
            }
          },
          "body": {
            "callback_url": "@{listCallbackUrl()}"
          },
          "path": "/trigger/mailmessage/onnewemail"
        }
      }
    },
    "actions": {
      "Parse_Email_Content": {
        "type": "ParseJson",
        "inputs": {
          "content": "@triggerBody()?['body']",
          "schema": {
            "type": "object",
            "properties": {
              "productName": { "type": "string" },
              "price": { "type": "number" },
              "category": { "type": "string" }
            }
          }
        }
      },
      "Create_Product_via_Custom_Connector": {
        "type": "ApiConnection",
        "inputs": {
          "host": {
            "connection": {
              "name": "@parameters('$connections')['productcatalog']['connectionId']"
            }
          },
          "method": "post",
          "path": "/products",
          "body": {
            "name": "@body('Parse_Email_Content')?['productName']",
            "price": "@body('Parse_Email_Content')?['price']",
            "category": "@body('Parse_Email_Content')?['category']"
          }
        }
      },
      "Send_Confirmation_Email": {
        "type": "ApiConnection",
        "inputs": {
          "host": {
            "connection": {
              "name": "@parameters('$connections')['office365']['connectionId']"
            }
          },
          "method": "post",
          "path": "/v2/Mail",
          "body": {
            "To": "@triggerBody()?['from']",
            "Subject": "Product Created Successfully",
            "Body": "Product @{body('Create_Product_via_Custom_Connector')?['name']} has been created."
          }
        }
      }
    }
  }
}

Power Apps Integration

Use the connector in a Power App:

// Power Apps formula examples

// Load products on screen load
Set(
    varProducts,
    ProductCatalog.GetProducts({
        category: Dropdown1.Selected.Value,
        page: 1,
        pageSize: 50
    }).products
);

// Create new product
Set(
    varNewProduct,
    ProductCatalog.CreateProduct({
        name: txtName.Text,
        description: txtDescription.Text,
        price: Value(txtPrice.Text),
        category: ddCategory.Selected.Value
    })
);

If(
    !IsBlank(varNewProduct.id),
    Notify("Product created successfully", NotificationType.Success),
    Notify("Failed to create product", NotificationType.Error)
);

// Update product
ProductCatalog.UpdateProduct(
    Gallery1.Selected.id,
    {
        name: txtEditName.Text,
        price: Value(txtEditPrice.Text),
        inStock: chkInStock.Value
    }
);

// Delete product with confirmation
If(
    Confirm("Are you sure you want to delete this product?"),
    ProductCatalog.DeleteProduct(Gallery1.Selected.id);
    Refresh(varProducts)
);

OAuth 2.0 Authentication

Configure OAuth for secure authentication:

securityDefinitions:
  oauth2:
    type: oauth2
    flow: accessCode
    authorizationUrl: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize
    tokenUrl: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
    scopes:
      api://your-api-client-id/.default: Access the API

Summary

Power Platform custom connectors enable:

  • API integration without code
  • Citizen developer empowerment
  • Reusable connections across apps
  • OAuth and API key authentication
  • Policy-based request transformation

Bridge your APIs to the low-code world and accelerate digital transformation.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.