Find The Object Type Of Any Id Value Using Flows

If you’ve been in the Salesforce ecosystem for long enough, you’ll eventually find yourself working with Record IDs that could be multiple types of objects, and you need to build a report or automation that needs to know what kind of object it is. A common example of this might be a task or activity report grouped by the Type of object each Task is related to. It usually involves adding a Formula(Text) field to the Activity object that translates the record Id in the Related To field on Task/Activity to the name of the Type of Object it is:

That kind of formula gives you a field on the Activity object that will tell you what kind of object it is related to! You can imagine all of the nifty reports this could enable, or Flow-based automation you could build that may be specific to Activities that are related to a particular kind of object.

For many years, it was really the best we could do without writing Apex code. In this post, we will walk through the limitations of this approach, and how to build a better version of this in a Record-Triggered Flow.

A better approach with Flows

With today’s automation tools, and Flow Builder in particular, we can account for any object, without any hardcoding, and without a single line of Apex. The Flow itself is simple: just two elements in a Record-Triggered Flow. So if you’re short on time and want to skip the explanations, jump on ahead to “Let’s Build”, and be on your way. But I think its helpful to know the how and the way, so let’s dive in.

Secret Sauce Ingredient 1: Key Prefix

Record ID values are 15 or 18 digit values that uniquely identify Salesforce records. We won’t go into everything that goes into constructing them, except for one crucial bit of information: the first three characters of every ID identifies the object type of that ID! This is called the Key Prefix. Each object – standard or custom – has a unique 3 character Key Prefix and *every record* in that object starts with those three characters. That’s why the formula above takes the first three characters of the ID LEFT(WhatId,3) and then uses the CASE() function to translate that to an object name.

There are some important things to know about the Key Prefix:

  • Standard Salesforce Objects (objects that don’t have a suffix like __c at the end) all each have the same key prefix in every org in every environment across all of Salesforce. Account is always 001, Opportunity is always 006, etc
  • Custom Objects are not guaranteed to be the same across orgs. Even though you may find some of your custom objects are the same between Production and Sandbox, this is not guaranteed and should not be relied upon.

Given this information, have you caught the limitations of the formula approach yet? There’s two huge red flags:

  • It requires you to hardcode the 3 digit key prefix to object translations! If you build this formula field in Sandbox (which you should definitely be doing!) to include a Custom Object, there is no guarantee that the same object will have the same key prefix when you move it to production, leaving you scratching your head, then needing to make a change directly in Production (which you should definitely NOT be doing!)
  • It requires you to explicitly write out each object you want to translate from key prefix into an object name. This means writing out a /massive/ formula to account for all your objects, or lots of maintenance when someone inevitably asks why it doesn’t work for their favorite custom object that wasn’t included.

There is a better way. Enter Flows, and Secret Sauce Ingredient 2.

Secret Sauce Ingredient 2: EntityDefinition

When building automation in Flow Builder, we all know you can retrieve a record, or collections of records, from every day objects like Account and your Custom Objects, but did you know there are a whole host of system objects you can retrieve records from using a simple Get element? One of these objects is what is going to help us today: EntityDefinition. This object contains metadata and configuration information about each of the Standard and Custom Objects in your org. Each object has a record in this EntityDefinition object. Information like Label, API Name, Plural Label, and more.

And – you guessed it – EntityDefinition has a field with each object’s Key Prefix.

Let’s build!

Now that we’ve got our two ingredients, let’s build a better alternative to that formula above. Let’s take one moment to ask ourselves what we need first:

We want a field on Activity that contains the name of the Object Type for the record it has in its WhatId field (the label for the WhatId field is “Related To”).

Add a Text Field to Task

Instead of a formula field, we’re going to add a regular Text field to the Activity object, and we’re going to call it something like “Related To Type” (that label is my preference, but whatever makes sense to you and your team)

Add a Record-Triggered Flow on Task

Entry Criteria

Next we’re going to build some automation to populate this field. We’ll add a Record-Triggered Flow on the Activity Object to do this. Since an existing Task could be updated to change which record is in the Related To field, we’ll want this Flow to run when records are Created and Updated

And we’ll want to configure Entry Criteria so we’re only running this Flow when we need to, and not when we don’t. We can use the (new as of Summer 22 release) Entry Criteria Formula feature to do this.

In short, this Entry Criteria formula will ensure this Flow only runs if its a newly created Task record that has a WhatId value, or if its an update on an existing Task and this update includes a change to the WhatId value, which are the scenarios we’ll need to set or change our Related To Type field.

(ISNEW() && NOT(ISBLANK({!$Record.WhatId})))
 || ISCHANGED({!$Record.WhatId})

Lastly for the Start Configuration, don’t forget to take advantage of our ability to run this as a Fast Field Update, since all we are doing is setting a value in a field on the same object that triggered the flow.

Get the Related EntityDefinition

Without further ado, let’s use our secret sauce ingredients to build this Flow. The Flow has our $Record variable with the WhatId, and the EntityDefinition object is what has the link we need between the KeyPrefix from that $Record.WhatId and the Object’s name.

So we want a Get on EntityDefinition, and we’ll add filter conditions to match the KeyPrefix against a Formula Resource that returns the first 3 characters of $Record.WhatId field

Formula Resource (named relatedToKeyPrefix):


GOTCHA ALERT: There are a handful of standard, system objects listed in EntityDefinition that have no KeyPrefix value. They cannot be associated with Tasks, so we don’t really need to worry about needing to match on them. But, we do want to clear our field if a task gets its related to field cleared to be empty, we want to avoid accidentally matching on one of these “empty KeyPrefix” objects. Its a simple condition we need to add to the Get:

KeyPrefix Does Not Equal {!$GlobalConstant.EmptyString}

Next, in the Flow Builder after the Get, we’ll add an Update element that sets the Related To Type field on our Task record using the result of this Get! It really is that easy. The field from the EntityDefinition that we’ll use here is MasterLabel.

Back in the Flow Builder Canvas, our resulting flow is just two elements!

Now, you’re going to build a Flow Test for this before deploying to production, aren’t you?

⚠ Consider Existing Data ⚠

One difference from the formula approach, and consideration you’ll have to account for with this approach, is your existing data. With a formula field, you don’t have to make any data changes- its just a formula that is evaluated anytime someone or something looks at the record. With this approach, once the Record-Triggered Flow is in place, all records *moving forward* will have your new field populated, but your existing records won’t. This means you’ll need to backfill them, or tweak the entry criteria and run a one-time “backfill” update. Use whichever data tool you’re most comfortable using to do this.

Take it further

Add a Related To API Name

The object’s Label was useful for human-readable outputs like the reports we discussed in the beginning, but you may find a lot of use cases where its better to have a unique-and-still-human-readable value, which is what the API name is designed to be. So, add a Related To Type API Name text field to the Activity object, and simply add it alongside the Update element we already have for the Related To Type field.

Ultimate Picklist Approach

If you’re really clever, and you have a stable set of objects, you could instead use a picklist. If you have a picklist with values that equate to your object labels and api names, then your Flow just needs to update it with the EntityDefinition’s QualifiedApiName value, and then you get the best of both worlds in one field. Reports and Record Views show the label (and can even be translatable!), while can manage it using api name.

Decision Branching

This becomes very powerful if you’ve got different actions you’ll need to take based on different objects, because you can simply add Outcomes to the decision for each object type, and configure its path with Object Type-specific actions accordingly.