5

I know it is fairly straightforward in Apex to determine if the Salesforce instance is production org or a Sandbox org, but I wonder if the same is possible via formula? I have also read What are Salesforce ID's composed of? but it seems to be of little assistance in parsing $Organization.Id to get the desired information. Every sandbox I have checked was created in a production instance.

Examples with the pod and first 6 characters of their Organization Id:

cs45 - 00D8A0XXXXXXXXX
cs19 - 00D290XXXXXXXXX

I want to know if it is possible to write a formula that can differentiate org type without any post-refresh steps. I would also like to avoid hard coding any Id values.

It would be fairly straightforward to create a Hierarchy Custom Setting and populate the IsSandbox data into it via a SandboxPostCopy implementation. Is there no simpler, more reliable way? Admins down the road may forget to run the script.

Adrian Larson
  • 149,971
  • 38
  • 239
  • 420

7 Answers7

9

You should be able to write a formula that will expose a line saying "Production" or "Sandbox" by using your production id from Administer->Company Information.

There are limitations using formulas that will not allow us to access all system fields. My suggestion, and based on your request of using a formula, is to create a field of data type formula that will compare the running Organization.Id with your production id. I don't know all your requirements but this is a simple approach that perhaps can be of some help.

If we take this production id:

cs45 - 00D8A0XXXXXXXXX

We can add a simple formula on any object to obtain, or print out, if an instance is a sandbox or if it is production environment. Depending your requirements you need to select the object the better suits your logic. Let's say that you add this field on the User object.

Field data type: "Formula"

Formula Return Type: "Text"

On the "Advanced Formula" canvas add this formula:

IF( $Organization.Id !='00D8A0XXXXXXXXX', 'Sandbox', 'Production')

This is the most simple approach I can think off.

Hope it helps.

Carlos Naranjo
  • 3,919
  • 4
  • 28
  • 32
  • The problem with this is that it seems that on Sandbox refresh, references to the Prod Org ID are replaced with the new Sandbox ID. This happens without changing the Last Mod date/user on the Formula field. – Thomas Taylor Mar 15 '18 at 20:00
  • See: https://help.salesforce.com/articleView?id=data_sandbox_implementation_tips.htm "Salesforce inserts the new value in any place the org ID is used, such as text values and metadata." – Thomas Taylor Mar 15 '18 at 20:12
6

This is by no means a perfect solution as there are definitely limitations but will work in some orgs

/* True if a sandbox */ 
CONTAINS($User.UserName,'@mycompany.com.')

Here, the assumption is that in PROD, usernames are all xxx@mycompany.com but in any sandbox, they will be xxx@mycompany.com.sandboxname. Note the extra period

If your company is a .org or a .co.uk, etc. change accordingly

Definitely fragile if users are added manually to sandbox without following conventions of appending the .sandboxname or if usernames in PROD aren't always ending in @mycompany.com

Not for use in (un)managed packages.

cropredy
  • 71,240
  • 8
  • 120
  • 270
  • Yeah this had occurred to me, I like it but was curious if there is anything better out there. Is there an ENDSWITH equivalent? – Adrian Larson May 04 '16 at 00:05
  • 1
    @AdrianLarson : I felt 'dirty' even writing this answer as it has to be heavily caveated. You could probably also use REGEX(..) for more flexibility – cropredy May 04 '16 at 00:08
  • We tried this once. It worked great until people started setting up users in sandboxes without specifying the sandbox name... Of course, you could now write an Apex Trigger to, you know, verify if they're in a sandbox or not. – sfdcfox May 04 '16 at 04:45
  • @sfdcfox. I agree. Would require trigger on User to enforce username discipline. – cropredy May 04 '16 at 04:47
  • 1
    I actually used NOT(CONTAINS(LOWER(Username), ".com.")) as a formula on the User object. Now I can just merge in $User.Is_Production__c. – Adrian Larson May 20 '16 at 16:33
5

This will need some refinement in testing, but could probably be made to work.

Examine the API URL domain to see if it has a subdomain indicating a sandbox.

CONTAINS($Api.Partner_Server_URL_360, '.cs') || 
CONTAINS($Api.Partner_Server_URL_360, '\\cs')

The .cs is to handle custom domains and the \\cs for ones without.

Hat tip to @eyescream who had suggested a similar idea in Visualforce using the OrgId that made me think about looking for the domain.

I was going to suggest using a REGEX() in the forumla, but that doesn't appear to be available for a Checkbox formula.


Expanding on my comment.

From the OrgId, you could look at the pod identifier to figure out if it is a Sandbox org. See Converting the Salesforce Pod Id to ServerUrl/Name and partial solution in Apex that could potentially be converted to a formula.

To your example sandbox OrgIds, we now need to use the first two characters after the keyprefix to identify pod.

E.g.

  • 00680 -> NA6
  • 0068A -> CS45
  • 00620 -> EU0
  • 00D29 -> CS19

The big problem with this approach is that it needs updating every time Salesforce adds a new pod. It's really fragile, so don't come looking for me if your code mistakes a sandbox for a production org and does something unsavory.

Thomas Taylor
  • 5,784
  • 21
  • 42
Daniel Ballinger
  • 102,288
  • 39
  • 270
  • 594
3

I used in a formula if($Organization.Id ='00xxx000000xxxx' ... where 00xxx000000xxxx was Production Org id, only to find out that, in Sandbox, the text was substituted by the Sandbox Org id after refresh.

So my best alternative would be to split the text in 2 parts:

if( LEFT($Organization.Id,11) ='00xxx000000' && RIGHT($Organization.Id, 4) ='xxxx' ... This way the text will not be altered during refresh and I will not have to manually change the default value calculated after each refresh.

Marianna

Marianna
  • 31
  • 2
2

I noticed this is an old thread. However, would like to post the answer which worked for me and I hope it is more generic.

IF(CONTAINS(LEFT($Api.Partner_Server_URL_490, FIND( '/services', $Api.Partner_Server_URL_490)),'--'), 'Sandbox','Production')

$Api.Partner_Server_URL_490 will return the below Urls

Lightning Sandbox - https://YourOrgName--sandboxname.lightning.force.com/Services/Soap/v49.0/00Dxx

Classic Sandbox - https://YourOrgName--sandboxname.my.salesforce.com/Services/Soap/v49.0/00Dxx

Lightning Prod - https://YourOrgName.lightning.force.com/Services/Soap/v49.0/00Dxx

Classic Prod - https://YourOrgName.my.salesforce.com/Services/Soap/v49.0/00Dxx

So the formula gets the base url from their respective links, checks whether the hostname has '--' in it and if it has then it would be a Sandbox otherwise it will be Production.

0

This doesn't automatically distinguish between a Sandbox or Production, but for those who would like more control (say, you need to switch the instance name for whatever reason), one way to handle this is via a Custom Label that stores the instance name and reference it in a formula field. Like so:

Custom Label

SFDC_Instance_Name = na32 //change this to cs47 in sandbox

Formula Field

"https://" & $Label.SFDC_Instance_Name & ".salesforce.com/" & Id
Lightning Evangelist
  • 3,458
  • 3
  • 33
  • 67
0

Because $Organization.IsSandbox does not exist there to this day, the workaround by @gorav on his blog post is

  • Before your Decision "Add a Get Record" to your Flow, (name it "getOrganization") pinging the Organization Object
  • Then in your Decision have {!getOrganization.IsSandbox} Equals False