Understanding the Power Automate Definition
Tags: powerautomate, powerplatform, lowcode, json
Published: 2024-07-08 06:11:24

Power Automate is built on Azure Logic Apps so they both share the same definition schema. The definition is a json object that holds the information for Power Automate/Logic Apps to complete all the actions. 

Microsoft provides some good documentation on the definition ([https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-workflow-definition-language](https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-workflow-definition-language)), but I wanted to take a more detailed approach around Power Automate. This blog might not be for everyone, but if you were the type of child who had to open up the old toaster to see how it worked this could be for you.

A call out here is that the definition doesnt travel on its own very often, it also normally includes 'properties', 'connection references' and more, so I will start from a higher level and include these in the blog, this all combined is often called the definition file or clientData.

This blog will cover:

1. How to View the Definition
2. The Keys
3. Top Level Keys
4. Triggers
5. Actions
7. Variations

---

## 1. How to View the Definition/clientdata
Power Automate converts the definition schema into our flow so that we can edit it so its not that easy to see it, but there are a few ways.

**APIs**
Through the flow api:

```
https://us.api.flow.microsoft.com/providers/Microsoft.ProcessSimple/environments/{ENVIRONMENT_ID}/flows/{FLOW_ID_FROM_URL}?api-version=2016-11-01&$expand=swagger,properties.connectionreferences.apidefinition,properties.definitionSummary.operations.apiOperation,operationDefinition,plan,properties.throttleData,properties.estimatedsuspensiondata
```

![chrome dev tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubooiz64vqh6ogmexr9e.png)
_Shown here in Chrome Dev tools_

and the Dataverse api:
```
https://{URL_ENVIRONMENT_DYNAMICS_URL}/api/data/v9.2/workflows?$filter=resourceid eq '{FLOW_ID_FROM_URL}'
```

![dataverse api](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kkv0y0jzjsg52obvtcs1.png)
_Definition is in the 'clientdata' field, but only works on solution aware flows_

**Power Automate**
Using the Dataverse List rows on the workflows (Processes) table. Though this also only works on solution aware flows.

![flow get defintion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bka8oaagk9wxu5s5idmk.png)

**AutoReview**
AutoReview is a free Chrome Extension I created for code reviews, it also include the function to view the clientdata (either on current open flow or exported zip file).
[https://chromewebstore.google.com/detail/autoreview-for-power-auto/laaendfpgmhjilhjkbebekgdgfjaajif](https://chromewebstore.google.com/detail/autoreview-for-power-auto/laaendfpgmhjilhjkbebekgdgfjaajif)

![autoreview ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofrt44flizd2jl4a7opu.png)

**Power Automate Tools**
Another Chrome Extention but this one allows you to actually edit the schema definition directly (very cool 😎).
[https://chromewebstore.google.com/detail/power-automate-tools/jccblbmcghkddifenlocnjfmeemjeacc](https://chromewebstore.google.com/detail/power-automate-tools/jccblbmcghkddifenlocnjfmeemjeacc)

![power automate tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8y7w13y8v055f2csj9w.png)


**Exports**
Both the legacy and solution export zip files contain the definition file

## 2. The Keys

The current version of the definition key is [https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json](https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json). Depending on where the definition is accessed impacts what else is included, only the definition is consistent. So there are a lot of keys, but below is a list of the ones that I think are important:

- name
- id
- type
- connectionReferences
- isManaged
- properties/apiId
- properties/displayName
- properties/definition
- definition/metadata
- definition/$schema
- definition/parameters
- definition/triggers
- definition/actions

_parent/key_

## 3. Top Level Keys
The top level keys are kind of the metadata to the flow and not part of the actual definition, they give information about the flow but are not really part of it.

**Name**
Exampe:`name": "4b569087-cf7a-18bc-dfb4-f9b31fd7e635"`
This is the flow id, it is seen in the url and seen as resourceid in the workflow table (solution aware). This is not the guid for the workflow table (workflowid), but the guid witin Power Automate.

**Id**
Example: `id": "/providers/Microsoft.ProcessSimple/environments/Default-6b6c3ede-aa0d-4268-a46f-96b7621b13a8/flows/4b569087-cf7a-18bc-dfb4-f9b31fd7e635"`
Relative url path for the flow (Envirnoment id and flow name)

**Type**
` "type": "Microsoft.Flow/flows",`
Logic apps or flow

**Is Managed**
`isManaged": false`
Is the flow in a managed solution.

**Connection References**
``` javascript
"connectionReferences": {
      "shared_sharepointonline": {
        "connectionName": "shared-sharepointonl-594ec2f7-b783-4358-8a34-901d2cf18e0e",
        "source": "Invoker",
        "id": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline",
        "tier": "NotSpecified"
      }
```
Connection references used within the flow. The connectionReference is linked to the action in the flow with the first key (shared_sharepointonline in the above example). When there are multiple of the same connection they are incremented (e.g. shared_sharepointonline, shared_sharepointonline_1, shared_sharepointonline_2, and so on).

The connectionName is the guid for the actual connection reference. The id is the type of connection, so above shows it is for SharePoint. Tier flags if it is a premium connector.

_The following are all under the properties key so I stretched it with 'top level':_

**ApiId**
`"apiId": "/providers/Microsoft.PowerApps/apis/shared_logicflows"`
App on azure that processes the definition

**DisplayName**
`"displayName": "demo",`
Name of the flow shown in all of the menus etc

**Definition**
```javascript
"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": {
        "When_an_item_is_created": {
          "recurrence": {
            "frequency": "Minute",
            "interval": 1
          },
          "splitOn": "@triggerOutputs()?['body/value']",
          "metadata": {
            "operationMetadataId": "44e16d66-9bad-4c13-b095-e0d1720f2a20"
          },
          "type": "OpenApiConnection",
          "inputs": {
            "host": {
              "apiId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline",
              "connectionName": "shared_sharepointonline",
              "operationId": "GetOnNewItems"
            },
            "parameters": {
              "dataset": "https://37wcqv.sharepoint.com/sites/testsite",
              "table": "742435b6-7897-4636-ab8e-ec347405b9a6"
            },
            "authentication": "@parameters('$authentication')"
          }
        }
      },
      "actions": {
        "Compose": {
          "runAfter": {},
          "metadata": {
            "operationMetadataId": "d725af82-56c4-435c-bcea-dba38ddf4e02"
          },
          "type": "Compose",
          "inputs": "@triggerOutputs()?['body/Title']"
        }
      }
    },
```
This is the contents of your flow, with triggers, connections and the actions you use.

## 4. Triggers
We are now inside the definition key, with the first key being triggers (yep plural, as it may bot be possible to have multiple in Power Automate but it is in Logic Apps). There 3 trigger schemas:

**Instant**
Instant (button/PowerApp/Copilot) have standard input schema with each input a item within properties. The item name is sequential of the type, so text,text_1,text_2 or number,number_1.
``` javascript
"triggers": {
    "manual": {
        "metadata": {
            "operationMetadataId": "4c3b12d2-87bf-45e3-a766-9555de826c05"
        },
        "type": "Request",
        "kind": "Button",
        "inputs": {
            "schema": {
                "type": "object",
                "properties": {
                    "text": {
                        "title": "name",
                        "type": "string",
                        "x-ms-dynamically-added": true,
                        "description": "Please enter your input",
                        "x-ms-content-hint": "TEXT"
                    },
                    "boolean": {
                    "title": "Yes/No",
                    "type": "boolean",
                    "x-ms-dynamically-added": true,
                    "description": "Please select yes or no",
                    "x-ms-content-hint": "BOOLEAN"
                },
                "text_1": {
                    "title": "Input 1",
                    "type": "string",
                    "x-ms-dynamically-added": true,
                    "description": "Please enter your input",
                    "enum": [
                        "First option",
                        "Second option"
                    ],
                    "x-ms-content-hint": "TEXT"
                },
                "number": {
                    "title": "Number",
                    "type": "number",
                    "x-ms-dynamically-added": true,
                    "description": "Please enter a number",
                    "x-ms-content-hint": "NUMBER"
                },
                "date": {
                    "title": "Trigger date",
                    "type": "string",
                    "format": "date",
                    "x-ms-dynamically-added": true,
                    "description": "Please enter or select a date (YYYY-MM-DD)",
                    "x-ms-content-hint": "DATE"
                },
                "email": {
                    "title": "Email",
                    "type": "string",
                    "format": "email",
                    "x-ms-dynamically-added": true,
                    "description": "Please enter an e-mail address",
                    "x-ms-content-hint": "EMAIL"
                },
                "file": {
                    "title": "File Content",
                    "type": "object",
                    "x-ms-dynamically-added": true,
                    "description": "Please select file or image",
                    "x-ms-content-hint": "FILE",
                    "properties": {
                    "name": {
                        "type": "string"
                    },
                    "contentBytes": {
                        "type": "string",
                        "format": "byte"
                    }
                }
            }
        },
        "required": [
            "text",
            "boolean",
            "text_1",
            "number",
            "date",
            "email"
        ]
    }
}
```

**Schdeuled**
Schedule are the simplist, with a recurrance key setting frequency and interval, so below is every 20 minutes.
``` javascript
"triggers": {
    "Recurrence": {
        "recurrence": {
            "frequency": "Minute",
            "interval": 20
        },
    "metadata": {
        "operationMetadataId": "7331237b-733a-4f27-b47c-1858ff22e2b0"
    },
        "type": "Recurrence"
    }
},
```

**Automated**
Automated show the trigger name as the main key, with recurrence being how often it is pinged to check for an event (different licenses,connectors and even enviornments can impact the frequency, this is why there is a test option as it ramps frequency to the seconds).
Options like splitOn and secure inputs are shown as keys within the trigger, along with connection type (OpenApiConnection). Finally is the parameters, which is the inputs to the connection, below is the SharePoint site and list id.
``` javascript
"triggers": {
    "When_an_item_is_created": {
        "recurrence": {
            "frequency": "Minute",
            "interval": 1
        },
        "splitOn": "@triggerOutputs()?['body/value']",
        "metadata": {
            "operationMetadataId": "51079ae2-e43a-4424-a687-d56dd5d511c5"
        },
        "type": "OpenApiConnection",
        "inputs": {
            "host": {
                "apiId": "/providers/Microsoft.PowerApps/apis/shared_sharepointonline",
                "connectionName": "shared_sharepointonline",
                "operationId": "GetOnNewItems"
            },
        "parameters": {
            "dataset": "https://sharepoint.com/sites/Sites3/",
            "table": "7f8831ea-d237-412e-ac15-0f0777992d1f"
        },
        "authentication": "@parameters('$authentication')"
    }
}
```

## Actions
_For referece, actions is a key that is collection of actions, so any action key is within the actions key, sorry I know that probably makes no sense_

Actions is all of the actions within the flow. Obviously this is the big one, and there are lots of variations of the action key, but there are 3 main groups, OpenApiConnection, Containers, Operations. Actions is an object not an array, so every action is an object within the object. It is also recursive, with containers having their own actions (see more later). The biggest call out is the run order, this is not the order in the object, that is the order they were added to the flow. The run order is actually backward, its based on the runAfter key. So in theory you start with the last action and find the item it ran after, and work your way back up the flow.

You can also peek the code of your actions to see everything.

![peek code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lfifsgkm06qqilylg4e8.png)

**OpenApiConnection**
The name of the action is the key, and it has the following keys:

- runAfter: Object that can contain muliple actions (branch merge) and has an array of run after conditions (Succeeded,Failed,Timedout,Skipped).
- metadata/operationMetadataId: if of the action, e.g SharePoint GetItems will all have same operationMetadataId.
- inputs/host/connectionName: this is the reference used to link to the connection references, so this the key to creating a relationship with the connectionReferences key.
- inputs/host/operationId: the type of api action, e.g GetItem would include SharePoint Get Item, Excel Get a row, Dataverse Get a row by ID.
- parameters: all of the inputs within the action, this will often not be representive of the ui, so you may select a file name but the input be a file id.
- secureData: for securing inputs/outputs of the action, it has properties key that is an array for inputs/outputs.
- runtimeConfiguration: for returning arrays (like GetItems/ListItems), only shown if pagination is turned on, shows the minimumItemCount key for how many rows per page
- retryPolicy: what retry's the action does if fails (does not show if set to default). Includes type key (fixed,none,exponential), and for each different types will show different inputs

``` JavaScript
"Get_a_row_by_ID": {
    "runAfter": {
        "List_rows": [
            "Succeeded"
        ]
    },
    "metadata": {
        "operationMetadataId": "bccfb3ea-e703-468d-a216-0b978ef670d9"
    },
    "type": "OpenApiConnection",
    "inputs": {
        "host": {
        "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps",
        "connectionName": "shared_commondataserviceforapps",
        "operationId": "GetItem"
        },
        "retryPolicy": {
            "type": "fixed",
            "count": 1,
            "interval": "PT20S"
        },
        "parameters": {
            "entityName": "workflows",
            "recordId": "@outputs('List_rows')?['body/value'][0]?['workflowid']"
        },
        "authentication": "@parameters('$authentication')"
    }
},
```
**Containers**
Containers are actions that have internal actions: Scope, Condition, Switch, ApplyToEach, DoUntil. They are all a little different, mainly around the branching logic within them. They also mess with our run order, as they break the rule for the first action within each branch. These run by position within the container, not runAfer (as runAfter is used for the container.

- type: what type of container
- actions: contains all of the actions within the container
- runAfter: same as before
- expression (not for Scopes): the input for the container 
- else (condition Only): the branch for false, same structure as actions
- cases (switch Only): each switch branch sits within the cases key, with Case, Case 2 etc and a Default. Each Case has anothor key case for the condition match and its own actions
- foreach: (ApplyToEach Only) array input

``` javascript
"Condition": {
    "actions": {
        "Get_a_row_by_ID": {
            "type": "OpenApiConnection",
                "inputs": {
                    "parameters": {
                    "entityName": "accounts",
                    "recordId": "12345"
                },
                "host": {
                    "apiId": "/providers/Microsoft.PowerApps/apis/shared_commondataserviceforapps",
                    "connectionName": "shared_commondataserviceforapps",
                    "operationId": "GetItem"
                },
                "authentication": "@parameters('$authentication')"
            }
        }
    },
    "runAfter": {},
    "else": {
        "actions": {
            "Compose": {
            "type": "Compose",
            "inputs": "hello world"
            }
        }
    },
    "expression": {
        "and": [
            {
                "equals": [
                    "",
                    ""
                ]
            }
        ]
    },
        "type": "If"
    }
}
```

**Operations**
Operations are the standard actions like setVariable and compose. 

type: type of operation
runAfter: same as before
inputs: inputs for the action, specific to each operation

``` javscript
Select": {
    "runAfter": {
        "For_each": [
            "Succeeded"
        ]
    },
    "type": "Select",
    "inputs": {
        "from": "@outputs('Get_items')?['body/value']",
        "select": {
            "@{item()?['Title']}": "test"
        }
    }
}
```

---

I have barely scratched the surface of the definition json (and Im sure I have a few things wrong as a lot was deduced from nosing around). Understanding it may not be necessary, but I find this knowledge realy useful when things don't work as expected, and I also find it kind of cool to get into the nuts and bolts of how things work, and as you made it to the end I suspect you do too 😎