Overview

The RecordType metadata type (objects/<ObjectName>/recordTypes/<RecordTypeName>.recordType-meta.xml) lets you segment a single Salesforce object into distinct business lifecycles. Each RecordType controls four UI and process layers: available picklist values (picklistValues), Page Layout assignments (mapped in Profile), Compact Layout overrides (compactLayoutAssignment), and Stage/Status Business Processes (businessProcess) on Opportunity, Case, Lead, and Solution.

A critical architectural rule: Record Types are a UI and process segmentation tool, not a security boundary. Removing a RecordType from a user’s Profile or PermissionSet only prevents the user from creating new records with that record type; if Organization-Wide Defaults or Sharing Rules grant read access to the record, the user can still view and report on it.

layoutAssignments recordTypeVisibilities (default = true) recordTypeVisibilities (visible = true) RecordType (.recordType-meta.xml) picklistValues (Subset of CustomField Picklist Options) compactLayoutAssignment (Highlights Panel Override) businessProcess (Required on Opportunity, Case, Lead) Profile (.profile-meta.xml) PermissionSet (.permissionset-meta.xml)

As shown in the diagram above, the .recordType-meta.xml file owns the allowed picklistValues, compactLayoutAssignment, and businessProcess, while Profile and PermissionSet metadata govern which users can create records with that RecordType and which Layout renders.

Prerequisites

  • Permissions: “Customize Application” permission to deploy record types.
  • Picklist Values Must Exist First: Every picklist option referenced inside <picklistValues> in a RecordType must exist on the corresponding CustomField or StandardValueSet.
  • API Version: Verified against API version 68.0 (Winter ’27).

Key RecordType XML Elements

XML Element Type Required Description & Architectural Rules
fullName string ✅ Yes Record type DeveloperName (Enterprise_Invoice). Qualified as Invoice__c.Enterprise_Invoice in package.xml.
active boolean ✅ Yes Whether the record type is active (true or false). Inactive record types are ignored when retrieving Profiles/PermissionSets.
label string ✅ Yes User-facing label displayed in the record type selection modal.
businessProcess string Conditional Required on Lead, Opportunity, Case, and Solution (e.g., <businessProcess>Enterprise Sales Process</businessProcess>). Not allowed on custom objects.
compactLayoutAssignment string No Overrides the object’s default compact layout for this specific record type.
picklistValues RecordTypePicklistValue[] No Lists every picklist field on the object and the subset of URL-encoded <values> enabled for this record type, including one <default>true</default> option.

Warning

In <picklistValues>, Salesforce URL-encodes special characters inside <fullName> (for example, Closed - Won is stored as Closed%20-%20Won or Net 30 / 60 as Net%2030%20%2F%2060). Hand-editing URL-encoded picklist values inaccurately causes Picklist value: X not found deployment errors.

Querying RecordType via SOQL and Zero-SOQL Apex Schema

1. Zero-SOQL RecordTypeId Resolution in Apex (Best Practice)

Never run [SELECT Id FROM RecordType WHERE DeveloperName = '...'] in Apex triggers or controllers. Use Schema.SObjectType methods, which execute in sub-millisecond time from memory and consume 0 SOQL queries:

Apex
Id enterpriseRtId = Schema.SObjectType.Invoice__c
    .getRecordTypeInfosByDeveloperName()
    .get('Enterprise_Billing')
    .getRecordTypeId();

2. Query All Record Types and Business Processes via Standard SOQL

When auditing record types across objects via the CLI or integration scripts, query the standard RecordType object:

SOQL
SELECT Id, SobjectType, DeveloperName, Name, IsActive,
       BusinessProcessId, Description, LastModifiedDate
FROM RecordType
WHERE SobjectType IN ('Account', 'Opportunity', 'Invoice__c')
ORDER BY SobjectType ASC, DeveloperName ASC

Example

The following Enterprise_Billing.recordType-meta.xml file defines an active record type on Invoice__c, assigns a dedicated Compact Layout, and restricts the Status__c picklist values.

force-app/main/default/objects/Invoice__c/recordTypes/Enterprise_Billing.recordType-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<RecordType xmlns="http://soap.sforce.com/2006/04/metadata">
    <fullName>Enterprise_Billing</fullName>
    <active>true</active>
    <compactLayoutAssignment>Invoice_Highlight_Compact_Layout</compactLayoutAssignment>
    <description>Used for B2B enterprise contracts with milestone billing and Net-60 terms.</description>
    <label>Enterprise Billing</label>
    <picklistValues>
        <picklist>Status__c</picklist>
        <values>
            <fullName>Draft</fullName>
            <default>true</default>
        </values>
        <values>
            <fullName>Pending%20Approval</fullName>
            <default>false</default>
        </values>
        <values>
            <fullName>Issued</fullName>
            <default>false</default>
        </values>
        <values>
            <fullName>Paid</fullName>
            <default>false</default>
        </values>
    </picklistValues>
</RecordType>

Deploy the record type along with its picklist field and permission set visibility:

Terminal
sf project deploy start \
  --metadata "CustomField:Invoice__c.Status__c" "RecordType:Invoice__c.Enterprise_Billing" \
  --target-org UATSandbox

Limits and Gotchas

  • Active Record Types Recommendation: Salesforce recommends no more than 200 active record types per object to avoid UI rendering and metadata serialization bottlenecks.
  • Deleting or Deactivating a RecordType: You cannot deactivate or delete a RecordType that is currently set as the <default>true</default> record type on any Profile. First update the Profile’s recordTypeVisibilities to change the default, then deactivate the RecordType.
  • Master RecordType (Master): Every object has a built-in Master record type used when no custom record types are assigned. You cannot assign both Master and a custom RecordType on the same object in a single Profile.

Frequently asked questions

Does RecordType restrict which records a user can view?

No. RecordType visibility on a Profile or PermissionSet only controls which record types a user can select when creating or editing a record. It does not restrict read access to existing records of that RecordType.

How do I get a RecordTypeId in Apex without using a SOQL query?

Use Schema.SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Enterprise_Customer').getRecordTypeId(). This reads from platform schema cache and consumes zero SOQL queries.

Why does deploying a RecordType on Opportunity, Case, Lead, or Solution fail with a missing businessProcess error?

RecordTypes on Opportunity, Case, Lead, and Solution require a businessProcess element referencing an active Sales, Support, Lead, or Solution Process defined on that object.

Why are new picklist values missing from my RecordType after deploying a CustomField?

Picklist value availability per RecordType is stored inside the RecordType (.recordType-meta.xml) file under picklistValues, not inside the CustomField file. You must deploy both the CustomField and the updated RecordType.

Does RecordType support the asterisk wildcard in package.xml?

No. Like CustomField and ValidationRule, RecordType requires explicit object-qualified names in package.xml (such as Opportunity.Enterprise_Deal) or retrieving the parent CustomObject.

Why are custom picklist values on Contact not retrieved in a Person Account RecordType?

Metadata API only retrieves standard picklist values on PersonAccount record types when the picklist is defined on Contact. Define shared custom fields on Account so they serialize cleanly into PersonAccount record types.

Comments

Questions, corrections and tips are welcome. Comments are GitHub Discussions, so you sign in with GitHub.