Retrieve Flow Metadata Synchronously with ‘Retrieve Flow Metadata’ Action

The community has built some powerful tools that enable flow metadata to be retrieved, modified and deployed. These tools were used, for example, in the unofficial Convert Process Builder tools. However, these tools make direct use of the metadata api, which is asynchronous. That means that they have to be deployed in the form of a screen flow that fires off the request and then sits there and polls for a result. That’s not useful if you’re running an autolaunched flow on the server.

Server-side flows can retrieve Flow metadata synchronously, via the Tooling API. This powerful interface is not as well known as the Metadata API, but has a lot of capabilities. One powerful facet of this API is that it returns the Flow metadata as a Metadata Apex object that can be manipulated via direct references in code:

This is, for many use cases, a lot nicer than parsing XML, which is what the Metadata api serves up.

I had a use case (involving the training of GPT models to generate flows) where I wanted the synchronous immediacy of the Tooling API (so I can trigger it on background events) and the XML return values that the Metadata API returns. It turns out that this is trivially easy, but it’s not well documented, and I had to consult a senior engineer to learn about it.

You can retrieve XML via the Tooling API, but you need to use the Accept header

The Tooling API is used via REST, and in REST calls to Salesforce, you can set the ‘Accept’ header like so:

req.setHeader(‘Accept’, ‘application/xml’);

This will not be done by default. Most Tooling API calls do not return a traditional SObject and the developers using those calls are happy to skip the XML and work with JSON. If you leave this header out and try to carry out XML parsing with what comes back, you’ll just get odd errors like this:

Error Occurred: An Apex error occurred: System.XmlException: Failed to parse XML due to: only whitespace content allowed before start tag and not { (position: START_DOCUMENT seen {… @1:1)

The Retrieve Flow Metadata action that’s installable below does this work for you, so you can simply insert it into a flow to retrieve flow xml synchronously, by flow name. Here’s where I set the REST header:

Querying by Flow Name is Not Intuitive

The second tricky bit I ran into involved my goal to query on the flowName. I didn’t have the ID. That’s a problem because the Tooling API has two modes and neither directly uses a name. ‘Easy Mode’ is focused on requesting records by their ID. Here’s an example:

If you want to use anything other than a recordId, the good news is that the Tooling API gives you the full power of SOQL queries. Here’s an example of how you query for a piece of metadata that’s confusingly named MetadataContainer:

At this point, I thought I was all set. I posted up this tooling query that queries by Flow Name :

url = baseUrl + '/services/data/v57.0/tooling/query/?q=Select+id+from+Flow+Where+FullName=\'' + flowName + '\'';

When I did this, though, I got this error message:

INVALID_FIELD Select id from Flow Where FullName='Test_MetadataRetrieval' ^ ERROR at Row:1:Column:27 field 'FullName' can not be filtered in a query call

A closer look at the documentation revealed that FullName is indeed explicitly disallowed in Tooling queries:

Thinking it through, this has to do with the fact that there are actually as many as 50 versions of metadata tied to a single flowName. The flowName is thus less unique than it might first appear.

The documentation does not make it clear just how you’re supposed to use multiple queries if you can’t query on name. However, the answer turns out to be to use Definition.DeveloperName:

String url = baseUrl + '/services/data/v57.0/tooling/query/?q=Select+id,Metadata+from+Flow+Where+Definition.DeveloperName=\'' + flowName + '\'';

FlowDefinition is a special object that basically stores which flow version is active. It isn’t used much these days.

With these new learnings, I was able to create Retrieve Flow Metadata action.


This action so far is unaware of version numbers. It’s just going to grab whatever is handed to it, which is going to be either the Active version or the Latest.

Authentication Considerations

This action provides a way to provide the name of a Named Credential. If you do that, it will use the NC. If one is not provided, it will try to use the SessionId. Be aware though that SessionId will only work for screen flows.


Version 1.0.2 6/5/23 Production Sandbox added Named Credential support

Previous Versions

Version 1.0 5/12/23 Production Sandbox First Release

View Source