Power Automate Web API

All flows are stored in Dataverse and leverage the rich Web API.

This article covers the management of flows included on the Solutions tab in Power Automate. Currently, flows under My Flows aren't supported by these APIs.

Compose HTTP requests

To get started creating requests, you'll need to first construct the URL. The format for the base URL of the Power Automate Web API is: https://{Organization ID}.{Regional Subdomain}.dynamics.com/api/data/v9.1/. The two parameters are:

  • Organization ID: A unique name for the environment that stores your flows.

  • Regional Subdomain: Depends on the location of your environment.

To get these two parameters:

  1. Go to the Power Platform admin center.

  2. Select the environment you use to build your flows.

    Screenshot of the environment for your flows.

  3. Copy the organization id and regional subdomain from the Environment URL field.

    Screenshot of the Organization id and region.

    You can also programmatically get the list of instances that are available to you using the Get Instances method in the online management API.

    Each request to the Web API must have the Accept and Content-type headers set to application/json.

  4. Populate the Authorization header with an Azure AD Bearer token.

    To learn how to get an Azure AD Bearer token for Dataverse, go to Use OAuth authentication with Microsoft Dataverse.

The following is an example of a request:

GET https://org00000000.crm0.dynamics.com/api/data/v9.1/workflows
Accept: application/json
Authorization: Bearer ey...

The response contains the list of flows from within that environment:

{
	"@odata.context": "https://org00000000.crm0.dynamics.com/api/data/v9.1/$metadata#workflows",
	"value": [{
		"@odata.etag": "W/\"12116760\"",
		"category": 5,
		"statecode": 0,
		"workflowidunique": "00000000-0000-0000-0000-000000000001",
		"workflowid" : "00000000-0000-0000-0000-000000000002",
		"createdon": "2018-11-15T19:45:51Z",
		"_ownerid_value": "00000000-0000-0000-0000-000000000003",
		"modifiedon": "2018-11-15T19:45:51Z",
		"ismanaged": false,
		"name": "Sample flow",
		"_modifiedby_value": "00000000-0000-0000-0000-000000000003",
		"_createdby_value": "00000000-0000-0000-0000-000000000003",
		"type": 1,
		"description": "This flow updates some data in Dataverse.",
		"clientdata": "{\"properties\":{\"connectionReferences\":{\"shared_commondataservice\":{\"source\":\"NotSpecified\",\"id\":\"/providers/Microsoft.PowerApps/apis/shared_commondataservice\",\"tier\":\"NotSpecified\"}},\"definition\":{...}},\"schemaVersion\":\"1.0.0.0\"}"
	}]
}

List flows

As shown previously, you can get the list of workflows by calling GET on workflows. Each workflow has many properties, but the most relevant are listed in the following table.

Property name Description
category The category of the flow. Here are the different categories.
0 - Classic Dataverse workflows.
1 - Classic Dataverse dialogs.
2 - Business rules.
3 - Classic Dataverse actions.
4 - Business process flows.
5 - Automated, instant or scheduled flows.
6 - Desktop flows.
statecode The status of the flow. The status can be 0 - off or 1 - on.
workflowidunique The unique identifier for this installation of the flow.
workflowid The unique identifier for a cloud flow across all imports.
createdon The date when the flow was created.
_ownerid_value The unique identifier of the user or team who owns the flow. This is an id from the systemusers table in Dataverse.
modifiedon The last time the flow was updated.
ismanaged Indicates if the flow was installed via a managed solution.
name The display name that you have given the flow.
_modifiedby_value The last user who updated the flow. This is an id from the systemusers table in Dataverse.
_createdby_value The user who created the flow. This is an id from the systemusers table in Dataverse.
type Indicates if the flow is a running flow, or a template that can be used to create more flows. 1 - flow, 2 - activation or 3 - template.
description The user-provided description of the flow.
clientdata A string-encoded JSON of the flow definition and its connectionReferences.

To learn more about properties, fields, and their usage, go to Process (Workflow) table/entity reference.

You can also request specific properties, filter the list of flows, and more, as described in Query data using the Web API. For example, this query returns only the automated, instant, or scheduled flows that are currently on:

GET https://org00000000.crm0.dynamics.com/api/data/v9.1/workflows?$filter=category eq 5 and statecode eq 1
Accept: application/json
Authorization: Bearer ey...

Create a cloud flow

Call POST on the workflows collection to create a cloud flow. The required properties for automated, instant, and scheduled flows are: category, name, type, primaryentity, and clientdata. Use none for the primaryentity for these types of flows.

You can also provide a description and statecode.

POST https://org00000000.crm0.dynamics.com/api/data/v9.1/workflows
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
		"category": 5,
		"statecode": 0,
		"name": "Sample flow name",
		"type": 1,
		"description": "This flow reads some data from Dataverse.",
		"primaryentity":"none",
		"clientdata": "{\"properties\":{\"connectionReferences\":{\"shared_commondataservice\":{\"connectionName\":\"shared-commondataser-00000000-0000-0000-0000-000000000004\",\"source\":\"Invoker\",\"id\":\"/providers/Microsoft.Power Apps/apis/shared_commondataservice\"}},\"definition\":{\"$schema\": \"https:\/\/schema.management.azure.com\/providers\/Microsoft.Logic\/schemas\/2016-06-01\/workflowdefinition.json#\",\"contentVersion\": \"1.0.0.0\",\"parameters\": {\"$connections\": {\"defaultValue\": {},\"type\": \"Object\"},\"$authentication\": {\"defaultValue\": {},\"type\": \"SecureObject\"}},\"triggers\": {\"Recurrence\": {\"recurrence\": {\"frequency\": \"Minute\",\"interval\": 1},\"type\": \"Recurrence\"}},\"actions\": {\"List_records\": {\"runAfter\": {},\"metadata\": {\"flowSystemMetadata\": {\"swaggerOperationId\": \"GetItems_V2\"}},\"type\": \"ApiConnection\",\"inputs\": {\"host\": {\"api\": {\"runtimeUrl\": \"https:\/\/firstrelease-001.azure-apim.net\/apim\/commondataservice\"},\"connection\": {\"name\": \"@parameters('$connections')['shared_commondataservice']['connectionId']\"}},\"method\": \"get\",\"path\": \"\/v2\/datasets\/@{encodeURIComponent(encodeURIComponent('default.cds'))}\/tables\/@{encodeURIComponent(encodeURIComponent('accounts'))}\/items\",\"queries\": {\"$top\": 1},\"authentication\": \"@parameters('$authentication')\"}}},\"outputs\": {}}},\"schemaVersion\":\"1.0.0.0\"}"
}

The most important section is the clientdata, which contains the connectionReferences that the flow uses, and the definition of the flow. The connectionReferences are the mappings to each connection that the flow uses.

There are three properties:

Property name Description
connectionName Identifies the connection. You can see the connectionName by going to the Connections page and then copying it from the URL of the connection.
source Either Embedded or Invoker. Invoker is only valid for instant flows (where a user selects a button to run the flow), and indicates that the end user will provide the connection. In this case, the connectionName is only used at design time. If the connection is Embedded, that means the connectionName you specify is always used.
id The identifier of the connector. The id always starts with /providers/Microsoft.PowerApps/apis/ and then has the connector name, which you can copy from the URL of the connection or by selecting the connector from the Connectors page.

Once you execute the POST request, you'll receive the OData-EntityId header, which will contain the workflowid for your new flow.

Update a cloud flow

You can call PATCH on the workflow to update, turn on, or turn off a cloud flow. Use the workflowid property to make these calls. For example, you can update the description and the owner of the flow with the following call:

PATCH https://org00000000.crm0.dynamics.com/api/data/v9.1/workflows(00000000-0000-0000-0000-000000000002)
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
	"description" : "This flow will ensure consistency across systems.",
	"ownerid@odata.bind": "systemusers(00000000-0000-0000-0000-000000000005)"
}

Note

The syntax for changing the owner uses the odata.bind format. This means instead of patching the _ownerid_value field directly, you append @odata.bind to the property name and then wrap the ID with systemusers().

In another example, you can turn on a cloud flow with this call:

PATCH https://org00000000.crm0.dynamics.com/api/data/v9.1/workflows(00000000-0000-0000-0000-000000000002)
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
	"statecode" : 1
}

Delete a cloud flow

Delete a cloud flow with a DELETE call:

DELETE https://org00000000.crm0.dynamics.com/api/data/v9.1/workflows(00000000-0000-0000-0000-000000000002)
Accept: application/json
Authorization: Bearer ey...

Note

You can't delete a cloud flow that's turned on. You must first turn off the flow or else you'll see this error: Cannot delete an active workflow definition. To learn more, go to Update a cloud flow in this article.

Get all users with whom a cloud flow is shared

Listing the users with access uses a function in Dataverse. This function takes a single parameter of Target:

GET https://org00000000.crm0.dynamics.com/api/data/v9.1/RetrieveSharedPrincipalsAndAccess(Target=@tid)?@tid={'@odata.id':'workflows(00000000-0000-0000-0000-000000000002)'}
Accept: application/json
Authorization: Bearer ey...

The Target parameter is a JSON-like string with a single property called @odata.id. Replace the workflow ID in the previous example. It returns:

{
    "@odata.context": "https://org00000000.crm0.dynamics.com/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.RetrieveSharedPrincipalsAndAccessResponse",
    "PrincipalAccesses": [
        {
            "AccessMask": "ReadAccess",
            "Principal": {
                "@odata.type": "#Microsoft.Dynamics.CRM.systemuser",
                "ownerid": "00000000-0000-0000-0000-000000000005"
            }
        }
    ]
}

Share or unshare a cloud flow

You can share a cloud flow using the GrantAccess action.

POST https://org00000000.crm0.dynamics.com/api/data/v9.1/GrantAccess
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
	"Target" : {
		"@odata.type": "Microsoft.Dynamics.CRM.workflow",
		"workflowid" : "00000000-0000-0000-0000-000000000002"
	},
	"PrincipalAccess": {
		"Principal": {
			"@odata.type" : "Microsoft.Dynamics.CRM.systemuser",
			"ownerid" : "00000000-0000-0000-0000-000000000005"
		},
		"AccessMask": "ReadAccess"
	}
}

The AccessMask parameter is a field with the following values for different permission levels:

Name Description
None No access.
ReadAccess The right to read the flow.
WriteAccess The right to update the flow.
DeleteAccess The right to delete the flow.
ShareAccess The right to share the flow.
AssignAccess The right to change the owner of the flow.

You can combine permissions with a comma. For example, you can provide the ability to read and update a cloud flow by passing ReadAccess,WriteAccess.

You can unshare a cloud flow with the RevokeAccess action. Here's an example:

POST https://org00000000.crm0.dynamics.com/api/data/v9.1/RevokeAccess
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
	"Target" : {
		"@odata.type": "Microsoft.Dynamics.CRM.workflow",
		"workflowid" : "00000000-0000-0000-0000-000000000002"
	},
	"Revokee": {
		"@odata.type" : "Microsoft.Dynamics.CRM.systemuser",
		"ownerid" : "00000000-0000-0000-0000-000000000005"
	}
}

RevokeAccess removes all permissions granted in the AccessMask.

Export flows

Use the ExportSolution action to export flows to a .zip file. First, add the flows that you want to a solution.

Once your flow is in a solution, call the following action:

POST https://org00000000.crm0.dynamics.com/api/data/v9.1/ExportSolution
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
	"SolutionName" : "Awesome solution 1",
	"Managed": false
}

ExportSolution returns a base 64-encoded string in the ExportSoutionFile property.

{
    "@odata.context": "https://org00000000.crm0.dynamics.com/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.ExportSolutionResponse",
    "ExportSolutionFile": "UEsDBBQAAgAI..."
}

You can then save this file into source control and/or use whatever version management or distribution system you want.

Import flows

Call the ImportSolution action to import a solution.

Property name Description
OverwriteUnmanagedCustomizations If there are existing instances of these flows in Dataverse, this flag needs to be set to true to import them. Otherwise they won't be overwritten.
PublishWorkflows Indicates if classic Dataverse workflows will be activated on import. This setting doesn't apply to other types of flows.
ImportJobId Provides a new, unique GUID to track the import job.
CustomizationFile A base 64-encoded zip file that contains the solution.
POST https://org00000000.crm0.dynamics.com/api/data/v9.1/ImportSolution
Accept: application/json
Authorization: Bearer ey...
Content-type: application/json
{
	"OverwriteUnmanagedCustomizations": false,
	"PublishWorkflows" : true,
	"ImportJobId" : "00000000-0000-0000-0000-000000000006",
    "CustomizationFile" : "UEsDBBQAAgAI..."
}

Since import is a long-running operation, the response to the ImportSolution action will be a 204 No content. To track the progress, call a GET on the importjobs object, providing the ImportJobId that you included in the original ImportSolution action.

GET https://org00000000.crm0.dynamics.com/api/data/v9.1/importjobs(00000000-0000-0000-0000-000000000006)
Accept: application/json
Authorization: Bearer ey...

This call returns the status of the import operation, including progress (the percentage of completion), startedon, and completedon (if import finished).

Once import has completed successfully, you'll need to set up the connections for the flow. The reason is that the connectionNames likely will be different in the destination environment (if the connections exist at all). If you're setting up new connections in the destination environment, then the owner of the flows must create them in the Power Automate designer. If the connections are already set up in the new environment, then you can PATCH the clientData of the flow with the names of the connections.

See also

Use the Microsoft Dataverse Web API with Power Apps