Identifying Flows by their HTTP trigger

TLDR; Here's the unmanaged solution to sweep your environments in search for flows triggered by an HTTP callback

ListflowswithHTTPcallback - Version:

If you ever found yourself debugging a third-party Flow with poor documentation, and this Flow calls another one using an HTTP request and you don't have a clue where this child Flow is *hidden*, you understand the reason behind this post. The situation is even worse when working with multiple environments with Flows spread among My Flows, Shared Flows and Solutions.

In this post, I will demonstrate how I saved several hours of Flow-hunting and headaches creating an auto-generated detailed catalogue of all the Flows in your organization that use HTTP triggers.

  1. Create a new manually triggered Flow with the first step using the connector List Environments as Admin (Power Platform for Admin). We are gonna get the HTTP-triggered flows from all environments.
List Environments as Admin (Power Plaform for Admin) connector
List Environments as Admin (Power Plaform for Admin) connector
  1. Create an empty variable of type Array and an Apply to Each right after to loop through all the returned environments:
Create variable and Apply to each loop
value: outputs('List_Environments_as_Admin')?['body/value']
  1. Let's get the Flows from every environment. Create a new step using List Flows as Admin V2 (Power Automate Management):
List Flows as Admin (Power Automate Management) connector
List Flows as Admin (Power Automate Management) connector
  1. Add dynamic content in the Environment field referencing the name property from the loop item:
Getting all the flows in the environment
Name: item()?['name']
  1. Now let's make sure we keep only the HTTP-triggered flows. Add a Filter array step:
Filter only flows triggered by http request
value: outputs('list_Flows')?['body/value']
item(): item()?['properties/definitionSummary/triggers']?[0]?['kind']
  1. Now add a conditional step to check if that are any flows left after the filter:
length(…): length(body('filter_http_flows_only'))
  1. Inside the If yes block, let's gather some info to make the final result more useful and user-friendly. First, we get the environment display name:
Getting the environment information
id: items('foreach_Environment')?['name']
displayName: items('foreach_Environment')?['properties/displayName']
  1. Then we create the new object with a Compose operation:
Creating main object
Body: body('filter_http_flows_only')
id: item()['name']
uri: item()['id']
displayName: item()['properties/displayName']
state: item()['properties/state']
environment: outputs('obj_environment')
creator: item()?['properties']?['creator']?['userId']
  1. The next step is to compose a new object merging the current array of the loop with the existing ones inside our variable:
Merging objects with previous loop
union(…): union(variables('flowsArray'), body('select_main_info'))
  1. And then store it back into the variable:
Setting the new array to the variable
Outputs: outputs('union_flows')

Ok, until this point we have the collection of Flows that are triggered by an HTTP request, but we don't have the actual URL that triggers the Flow. So let's get this information.

I will separate the next steps into two branches: The following one is essential for our results, but the other one contains only the Creator's information, so I will flag that part as optional. So let's create a Parallel branch to approach both parts:

  • Branch #1 – Getting the HTTP trigger URL
  1. Create another Apply to each loop that will fetch the trigger of each Flow in our list. Inside this loop, add a step with List Callback URL (Power Automate Management) followed by a Compose action. This will get the data and in sequence add it to the object as a new property called 'trigger':
Getting the http trigger URL for each flow inside a loop
flowsArray: variables('flowsArray')
environment/Id: items('foreach_http_flow')['environment/id']
addProperty(…): addProperty(item(), 'trigger', outputs('fetch_http_trigger_uri')?['body/response']?['value'])

[Important] I had cases where I had access to some environments but with limited permission, which resulted in the failure of the List Callback URL step. To prevent our Flow from crashing, the next step will have a custom Configure run after including 'has failed' cases. This step will remove empty triggers (just in case):

Filtering only successful results
Outputs: item()
item(): item()?['trigger']
Configure run after including 'has failed'
Make sure to include 'has failed'

At this point we already accomplished our main goal here: collect all the flows and their HTTP trigger URL. The output of our last action is the following, containing:

First output


id: unique ID of the flow
uri: partial URL to access the flow
displayName: title of the flow
state: current state of the flow
– id: unique ID of the environment
– displayName: friendly name of the environment
creator: unique ID of the author
trigger: the HTTP URL to trigger the flow

As you can notice, the creator's info is not that straightforward since we just have their unique ID. If that's enough, you can stop right there. But why not make it better? The following steps will describe how to fetch this information and add it to the final object. Let's do it!

  • Branch #2 (Extra) – Getting the author's information
  1. In the other branch, let's first get all the creators with a Select step, and then we execute a Union inside a Compose action to remove duplications:
Mapping creators from the flows
flowsArray: variables('flowsArray')
creator: item()['creator']
union(…): union(body('get_creators'), body('get_creators'))
  1. Now we apply another loop to get their names (and any other info you may want) using the connector Get User profile V2 (Office 365 Users), and collecting the result inside a Compose step in the sequence:
Getting creators info
Outputs: outputs('unique_creators')
Current item: item()
id: outputs('Get_user_profile_(V2)')?['body/id']
name: outputs('Get_user_profile_(V2)')?['body/displayName']
email: outputs('Get_user_profile_(V2)')?['body/mail']
  1. And then, outside of the loop, we collect the data using another Compose action:
Collecting creators data
Outputs: outputs('get_user_info')

Now that we have the users' (creator's) data, we have to add their info in the Flow objects as a new property. There are a few ways to achieve that, but since the other branch is going to take a few minutes to complete, there's no reason to complicate things here. So let's use another Apply to each loop (note that this action runs after the completion of both branches).

  1. Inside this loop, we add a Filter array to select the right user and then we add the info to the object with a Compose action:
Adding the creators info into the final objects
Body: body('filter_successful_results')
Outputs: outputs('creators')
id: item()['id']
creator: item('add_creator_info')['creator']
addProperty(…): addProperty(removeProperty(item(), 'creator'), 'creator', body('filter_autor'))
  1. And finally, let's collect everything in a single object:
Collecting final objects
Outputs: outputs('add_creator')

Voilà! After running our Flow, this is the final output:

Final output
Final output


id: unique ID of the flow
uri: partial URL to access the flow
displayName: title of the flow
state: current state of the flow
– id: unique ID of the environment
– displayName: friendly name of the environment
trigger: the HTTP URL to trigger the flow
– id: unique ID of the author
– name: author's display name
– email: email of the author

You can keep this result in a document, export it to CSV, or whatever way you prefer. You can also enhance this flow to search for a specific HTTP trigger instead of collecting all of them. It's all on you now.

Don't stop learning!

Cassio Milanelo

Skilled and self-motivated Dynamics 365 CE developer and Power Platform jack of all trades. Acknowledged for high productivity and enthusiasm. Slightly workaholic and coffee lover ☕

Leave a Reply