Get Record Types for the Running User
There's no single API call in Salesforce that answers "which record types can this user actually see?" You'd think there would be.
RecordType in SOQL gives you every active record type on an object but doesn't know which ones a user can actually use. Schema.RecordTypeInfo knows that, via isAvailable(), but doesn't give you queryable SObject records. So you need both, cross-referenced.
One small invocable closes the gap for any object you point it at.
// Inspired by Tamar Erlich's UnofficialSF action:
// https://unofficialsf.com/get-record-type-info-by-object-flow-action/
public with sharing class GetAvailableRecordTypes {
public class Request {
@InvocableVariable(
label='Object API Name'
description='The API name of the object to get record types for (e.g. Case).'
required=true
)
public String objectApiName;
}
public class Response {
@InvocableVariable(
label='Record Types'
description='Active, non-master record types available to the running user.'
)
public List<RecordType> recordTypes;
@InvocableVariable(
label='Default Record Type'
description='Default record type for the running user'
)
public RecordType defaultRecordType;
}
@InvocableMethod(label='Get Available Record Types' description='Returns active, non-master record types for the specified object that are available to the running user, plus the running user\'s default.')
public static List<Response> getAvailableRecordTypes(List<Request> requests) {
String objectApiName = requests[0].objectApiName;
Schema.DescribeSObjectResult describe = Schema.describeSObjects(new String[]{ objectApiName })[0];
Map<Id, Schema.RecordTypeInfo> infosById = describe.getRecordTypeInfosById();
Response response = new Response();
response.recordTypes = new List<RecordType>();
for (RecordType rt : [
SELECT Id, Name, DeveloperName
FROM RecordType
WHERE SObjectType = :objectApiName
AND IsActive = true
ORDER BY Name
]) {
Schema.RecordTypeInfo rtInfo = infosById.get(rt.Id);
if (rtInfo.isAvailable() && !rtInfo.isMaster()) {
response.recordTypes.add(rt);
if (rtInfo.isDefaultRecordTypeMapping()) {
response.defaultRecordType = rt;
}
}
}
return new List<Response>{ response };
}
}
The SOQL grabs every active record type for the object. isAvailable() filters that down to what the running user's profile allows. isMaster() drops the Master record type (always available, almost never what you want). isDefaultRecordTypeMapping() captures the user's default.
What it reproduces
When a user clicks the standard New or Change Record Type button, Salesforce shows a record-type chooser filtered to the types they're allowed to use, with their default pre-selected. That filtering isn't a single query you can run, it lives in Schema.RecordTypeInfo. This invocable reads exactly that, so the list it returns is the same set the user would see on those standard screens, and defaultRecordType is the same one Salesforce would pre-select.
So it hands you the pieces of the native picker, the available types and the default, in Apex or Flow, ready to render yourself.
It returns native RecordType SObjects, so Flow can feed the collection straight into a Choice Set or iterate its properties, and you can add fields to the query later without breaking anything that consumes it.
It's an @InvocableMethod, so a flow is the natural caller, but the same call works anywhere in Apex.
The test
Record types are org metadata you can't create in a test, so there's little to assert beyond confirming the call runs and hands back a list:
@isTest
public with sharing class GetAvailableRecordTypesTest {
@isTest
static void returnsAvailableRecordTypesForObject() {
GetAvailableRecordTypes.Request request = new GetAvailableRecordTypes.Request();
request.objectApiName = 'Case';
List<GetAvailableRecordTypes.Response> results =
GetAvailableRecordTypes.getAvailableRecordTypes(
new List<GetAvailableRecordTypes.Request>{ request }
);
System.assertEquals(1, results.size(), 'Expected one Response per invocation.');
System.assertNotEquals(null, results[0].recordTypes, 'recordTypes should always be a list, never null.');
}
}