How to Integrate Document Scanning in Power Apps Canvas Using a REST API

Microsoft Power Apps is a low-code development platform that enables you to build custom apps tailored to your business needs. Within Power Apps, a ‘Canvas App’ is a specific type of app that allows you to design your interface by dragging and dropping elements onto a canvas, similar to designing a slide in PowerPoint. While Canvas Apps do not natively support the embedding or execution of JavaScript code, they do allow for the invocation of services through Power Automate (Flow). This article aims to assist developers in integrating document scanning functionalities into Power Apps Canvas by utilizing the Dynamic Web TWAIN Service REST API.

What you’ll build: A Power Apps Canvas App that scans documents from a connected scanner using the Dynamic Web TWAIN Service REST API, orchestrated through Power Automate flows.

Key Takeaways

  • Power Apps Canvas Apps can integrate hardware document scanning through Power Automate flows calling a REST API, bypassing the JavaScript limitation.
  • The Dynamic Web TWAIN Service REST API exposes local TWAIN/WIA/eSCL scanners over HTTP, enabling any HTTP-capable client—including Power Automate—to trigger scans.
  • A lightweight Node.js proxy server bridges the gap between Power Apps (cloud) and the locally running Dynamic Web TWAIN Service.
  • This pattern applies to any enterprise workflow that needs to capture paper documents directly into a Power Apps form or data pipeline.

Common Developer Questions

  • How do I integrate document scanning into a Power Apps Canvas App without JavaScript?
  • Can Power Automate call a local scanner REST API to scan documents from Power Apps?
  • How do I expose the Dynamic Web TWAIN Service to the Internet for use with Power Apps?

This article is Part 1 in a 1-Part Series.

Prerequisites

Set Up a Web Server to Proxy Requests to the Dynamic Web TWAIN Service

Dynamic Web TWAIN Service is designed to operate within a local network via HTTP requests. To make it accessible from the Internet, a web server needs to be set up to forward requests to Dynamic Web TWAIN Service.

For a quick setup, you can download example code from the following GitHub repository: https://github.com/yushulx/dynamsoft-service-REST-API/tree/main/examples/REST.

To run the example code:

  1. Install Node.js.
  2. Open app.js and replace the license key with your own.

     license: "LICENSE-KEY",
    
  3. open a terminal and run the following commands:

     npm install
     node app.js
     > Server running at http://0.0.0.0:3000/
    
  4. Use ngrok to expose the local web server to the Internet.

     ngrok http 3000
    

    ngrok

  5. Visit the ngrok URL in a browser to verify that the web server is running properly.

    web server

Build a Document Scanning App with Power Apps Canvas

Step 1: Create a Blank Canvas App

Visit https://make.powerapps.com/ to create a Blank Canvas App.

Power App Canvas App

Step 2: Add UI Elements

The UI consists of two buttons, a dropdown list, and an image control.

Power App Canvas UI

  • The Get Devices button triggers an HTTP GET request to retrieve a list of available scanners.
  • The dropdown list displays the list of available scanners.
  • The Scan triggers an HTTP POST request to initiate document scanning.
  • The image control displays the scanned image.

Step 3: Create Power Automate Flows

According to our requirements, we create two flows: ScanDocument and GetDevices in Power Automate panel.

Power Automate flows

Create the GetDevices Flow (HTTP GET)

The GetDevices flow consists of four operations: HTTP, Parse JSON, Select, and Response.

GetDevices Flow

HTTP

Send an HTTP GET request to the web server. The URL is constructed with the ngrok URL and the /devices path.

power automate HTTP Action

Parse JSON

Parse the response body as JSON to extract the device information.

power automate parse JSON

The schema of the JSON response is as follows:

{
    "type": "object",
    "properties": {
        "devices": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "device": {
                        "type": "string"
                    },
                    "name": {
                        "type": "string"
                    },
                    "type": {
                        "type": "integer"
                    }
                },
                "required": [
                    "device",
                    "name",
                    "type"
                ]
            }
        }
    }
}

It can be automatically generated from the example response body:

{"devices":[{"device":"{\n\t\"deviceInfo\" : \n\t{\n\t\t\"Manufacturer\" : \"VFdBSU4gV29ya2luZyBHcm91cA==\",\n\t\t\"ProductFamily\" : \"U29mdHdhcmUgU2Nhbg==\",\n\t\t\"ProductName\" : \"V2luZG93cyBWaXJ0dWFsIFNjYW5uZXI=\",\n\t\t\"ProtocolMajor\" : 2,\n\t\t\"ProtocolMinor\" : 5,\n\t\t\"SupportedGroups\" : 0,\n\t\t\"Version\" : \n\t\t{\n\t\t\t\"Country\" : 1,\n\t\t\t\"Info\" : \"Mi41LjAgc2FtcGxlIHJlbGVhc2UgNjRiaXQ=\",\n\t\t\t\"Language\" : 2,\n\t\t\t\"MajorNum\" : 2,\n\t\t\t\"MinorNum\" : 5\n\t\t}\n\t},\n\t\"deviceType\" : 64,\n\t\"isSystemDefaultPrinter\" : false,\n\t\"name\" : \"V2luZG93cyBWaXJ0dWFsIFNjYW5uZXI=\"\n}\n","name":"Windows Virtual Scanner","type":64},{"device":"{\n\t\"deviceInfo\" : \n\t{\n\t\t\"BaudRate\" : \"\",\n\t\t\"Description\" : \"WSD Scan Device\",\n\t\t\"Driver Version\" : \"10.0.19041.1806\",\n\t\t\"Hardware Configuration\" : 0,\n\t\t\"Manufacturer\" : \"Microsoft\",\n\t\t\"Name\" : \"HPBCF77F (HP LaserJet Pro M329)\",\n\t\t\"PnP ID String\" : \"\\\\\\\\?\\\\swd#dafwsdprovider#urn:uuid:f8262726-dc64-5cf1-89ab-5e257f2d0f3a#http:##f8262726-dc64-5cf1-89ab-5e257f2d0f3a#scanservice#{6bdd1fc6-810f-11d0-bec7-08002be2092f}\",\n\t\t\"Port\" : \"urn:uuid:f8262726-dc64-5cf1-89ab-5e257f2d0f3a/http://f8262726-dc64-5cf1-89ab-5e257f2d0f3a/ScanService\",\n\t\t\"Remote Device ID\" : \"\",\n\t\t\"STI Driver Version\" : 3,\n\t\t\"STI Generic Capabilities\" : 49,\n\t\t\"Server\" : \"local\",\n\t\t\"Type\" : 65537,\n\t\t\"UI Class ID\" : \"{00000000-0000-0000-0000-000000000000}\",\n\t\t\"Unique Device ID\" : \"{6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\\\\0000\",\n\t\t\"WIA Version\" : \"2.0\"\n\t},\n\t\"deviceType\" : 32,\n\t\"isSystemDefaultPrinter\" : true,\n\t\"name\" : \"SFBCQ0Y3N0YgKEhQIExhc2VySmV0IFBybyBNMzI5KQ==\"\n}\n","name":"HPBCF77F (HP LaserJet Pro M329)","type":32},{"device":"{\n\t\"deviceInfo\" : \n\t{\n\t\t\"BaudRate\" : \"\",\n\t\t\"Description\" : \"HP OfficeJet Pro 6970 [01A3B2]\",\n\t\t\"Driver Version\" : \"10.0.19041.3271\",\n\t\t\"Hardware Configuration\" : 0,\n\t\t\"Manufacturer\" : \"Microsoft\",\n\t\t\"Name\" : \"HP OfficeJet Pro 6970 [01A3B2]\",\n\t\t\"PnP ID String\" : \"\\\\\\\\?\\\\swd#escl#59ca18cd-afb6-5c82-a201-23911ae167ea#{6bdd1fc6-810f-11d0-bec7-08002be2092f}\",\n\t\t\"Port\" : \"SWD\\\\Escl\\\\59ca18cd-afb6-5c82-a201-23911ae167ea\",\n\t\t\"Remote Device ID\" : \"\",\n\t\t\"STI Driver Version\" : 16777218,\n\t\t\"STI Generic Capabilities\" : 16,\n\t\t\"Server\" : \"local\",\n\t\t\"Type\" : 65537,\n\t\t\"UI Class ID\" : \"{00000000-0000-0000-0000-000000000000}\",\n\t\t\"Unique Device ID\" : \"SWD\\\\Escl\\\\59ca18cd-afb6-5c82-a201-23911ae167ea\",\n\t\t\"WIA Version\" : \"2.0\"\n\t},\n\t\"deviceType\" : 32,\n\t\"isSystemDefaultPrinter\" : false,\n\t\"name\" : \"SFAgT2ZmaWNlSmV0IFBybyA2OTcwIFswMUEzQjJd\"\n}\n","name":"HP OfficeJet Pro 6970 [01A3B2]","type":32},{"device":"{\n\t\"deviceInfo\" : \n\t{\n\t\t\"UUID\" : \"f8262726-dc64-5cf1-89ab-5e257f2d0f3a\",\n\t\t\"adminurl\" : \"http://HP6C02E0BCF77F.local.\",\n\t\t\"cs\" : \"binary,color,grayscale\",\n\t\t\"duplex\" : \"F\",\n\t\t\"is\" : \"platen,adf\",\n\t\t\"mopria-certified-scan\" : \"1.2\",\n\t\t\"name\" : \"HP LaserJet Pro M329 [BCF77F]\",\n\t\t\"pdl\" : \"application/octet-stream,application/pdf,image/jpeg\",\n\t\t\"representation\" : \"images/printer.png\",\n\t\t\"rs\" : \"eSCL\",\n\t\t\"service_type\" : 3,\n\t\t\"txtvers\" : \"1\",\n\t\t\"ty\" : \"HP LaserJet Pro M329\",\n\t\t\"vers\" : \"2.63\"\n\t},\n\t\"deviceType\" : 512,\n\t\"name\" : \"SFAgTGFzZXJKZXQgUHJvIE0zMjk=\"\n}\n","name":"HP LaserJet Pro M329","type":512}]}

Select

This operation extracts device information from the JSON object as the output.

power automate select

Response

Send the select output back to the Power App.

Power automate response

Create the ScanDocument Flow (HTTP POST)

The ScanDocument flow consists of four operations: Compose, HTTP, Parse JSON, and Return value(s) to Power Pages.

scan document flow

Compose

Input the selected scanner information from the Power App page.

power automate compose

HTTP

Append the compose output to the HTTP body and send an HTTP POST request to the web server. The URL is constructed with the ngrok URL and the /createJob path.

Parse JSON

Parse the response body as JSON to extract the scanned image.

power automate parse JSON

The schema of the JSON response is as follows:

{
    "type": "object",
    "properties": {
        "image": {
            "type": "string"
        }
    }
}

Return value(s) to Power Pages

Return the image name to the Power App page.

power automate Return value(s) to Power Pages

Step 4: Connect Power Automate Flows to the UI Elements

Get devices

Click on the Get Devices button and then assign the following code to its OnSelect property.

ClearCollect( Devices, GetDevices.Run());

Get device event

The code above will initiate the GetDevices flow when the Get Devices button is clicked. The flow’s response will populate the Devices collection, which can be found in the Variables panel.

power automate collection

Display device list

Click on the dropdown list and then assign Devices collection to its Items property. Then, set the display value to name in the properties panel.

power automate dropdown display

Acquire a document image

Click on the Scan button and then assign the following code to its OnSelect property.

Set(jsonString, Concatenate("{", """scan""", ":", DeviceList.Selected.Device, "}"));
Set(data, ScanDocument.Run(jsonString));

scan document event

The code constructs a JSON string with the selected scanner information and then initiates the ScanDocument flow. The data variable will be populated with the response from the flow and can be found in the Variables panel.

variable data

Display the document image

Set the Image property using the URL of the scanned image. The URL is constructed by combining the ngrok URL with the image name.

power automate image display

Step 5: Test the App

Scan document in Power App Canvas App

Common Issues and Edge Cases

  • Ngrok tunnel expires or disconnects: Free ngrok tunnels have session limits. If the Power Automate flow suddenly returns errors, restart ngrok and update the URL in your HTTP actions. For production use, consider a persistent reverse proxy or a static domain.
  • Scanner not listed in the dropdown: Ensure the Dynamic Web TWAIN Service is running and the scanner driver is installed. WIA and TWAIN drivers must be present on the host machine for the device to appear in the /devices response.
  • CORS or connection refused errors from Power Automate: Power Automate HTTP actions do not enforce CORS, but the proxy server must be reachable from the Internet. Verify ngrok is forwarding to the correct local port and that no firewall is blocking the connection.