The ReportType metadata type represents a Custom Report Type—the underlying data model template that determines which objects, join types (outerJoin), and field columns are available to users in the Lightning Report Builder. Available in API version 14.0 and later, ReportType supports standard, custom, external (__x), and Data Cloud (data_cloud, semantic_model) objects.
In Salesforce DX projects, custom report types are stored in force-app/main/default/reportTypes/ with the .reportType-meta.xml suffix.
Key Architectural Advantage: Lookup Field Spanning vs. Object Joins
A ReportType can join at most 4 objects top-down (Parent -> Child -> Grandchild -> Great-Grandchild). However, inside <sections><columns>, you can traverse upward across lookup relationships up to 4 levels deep using dot notation (for example, <field>Account__c.Parent.Owner.Department</field>). Always use Add Fields Related via Lookup (dot-notation <field> entries) for parent/sibling lookups so you save your 4 <join> slots for true 1-to-many child collections.
Key Metadata Fields
| Field | Location | Required | Description |
|---|---|---|---|
baseObject |
Root | Yes | Not updateable. Primary root object API name (Account, Invoice__c, etc.) |
label |
Root | Yes | Display name shown when choosing a Report Type |
category |
Root | Yes | Report Type folder category (accounts, opportunities, cases, other, data_cloud, flow) |
deployed |
Root | Yes | true (Deployed to end users) or false (In Development) |
description |
Root | No | Explains which object joins and lookup fields are included |
join |
Root / ObjectRelationship |
No | Recursive child relationship join (relationship plural name + outerJoin boolean) |
outerJoin |
ObjectRelationship |
Yes | true (“A with or without B” left outer join) or false (“A with B” inner join) |
sections |
Root | No | Drawer categories (masterLabel + columns) |
field |
columns |
Yes | Field API name (or dot-notation lookup path) |
table |
columns |
Yes | Object path (Account, Account.Contacts, Account.Contacts.Cases) |
checkedByDefault |
columns |
Yes | Automatically adds the column to newly created reports when true |
displayNameOverride |
columns |
No | Custom column alias shown in Report Builder |
Production-Grade XML Example
Below is a ReportType (Accounts_With_Contacts_And_Open_Cases.reportType-meta.xml) joining Account -> Contacts (inner join) -> Cases (left outer join) with cross-object lookup columns:
<?xml version="1.0" encoding="UTF-8"?>
<ReportType xmlns="http://soap.sforce.com/2006/04/metadata">
<baseObject>Account</baseObject>
<category>accounts</category>
<deployed>true</deployed>
<description>Accounts with Contacts and optional Cases, including Account Owner Manager lookup fields.</description>
<join>
<join>
<outerJoin>true</outerJoin>
<relationship>Cases</relationship>
</join>
<outerJoin>false</outerJoin>
<relationship>Contacts</relationship>
</join>
<label>Accounts with Contacts and Optional Cases</label>
<sections>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Name</field>
<table>Account</table>
</columns>
<columns>
<checkedByDefault>false</checkedByDefault>
<displayNameOverride>Account Executive Manager</displayNameOverride>
<field>Owner.Manager.Name</field>
<table>Account</table>
</columns>
<masterLabel>Account Details</masterLabel>
</sections>
<sections>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Name</field>
<table>Account.Contacts</table>
</columns>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>Email</field>
<table>Account.Contacts</table>
</columns>
<masterLabel>Contacts</masterLabel>
</sections>
<sections>
<columns>
<checkedByDefault>true</checkedByDefault>
<field>CaseNumber</field>
<table>Account.Contacts.Cases</table>
</columns>
<columns>
<checkedByDefault>false</checkedByDefault>
<field>Status</field>
<table>Account.Contacts.Cases</table>
</columns>
<masterLabel>Support Cases</masterLabel>
</sections>
</ReportType>Tip
Redeploying Deleted ReportTypes Preserves IDs: If a ReportType is accidentally deleted in an org, redeploying its .reportType-meta.xml with the same fullName via Metadata API restores the original ReportType record with its original Id—preventing broken report dependencies.
Frequently asked questions
How many objects can be joined in a Custom ReportType?
A Custom ReportType can join up to 4 objects hierarchically starting from baseObject through up to 3 nested join (ObjectRelationship) levels.
Why can I not place an inner join after an outerJoin in a ReportType?
In Salesforce Custom ReportTypes, once you select a left outer join (outerJoin = true, meaning With or Without related records) at one level of the object hierarchy, every subsequent child join below it must also have outerJoin set to true.
How do I include fields from a parent Lookup object in a ReportType without using one of the 4 object joins?
Add a columns entry inside a ReportLayoutSection where the field tag uses dot-notation spanning up to 4 lookup levels (such as Account.Owner.Manager.Name or Custom_Lookup__c.Status__c).
Why are new CustomFields not automatically visible in existing Custom ReportTypes?
Unless Auto-Add New Custom Fields to Custom Report Type Layouts is enabled in Reports and Dashboards Settings, new CustomFields must be explicitly added as columns inside the .reportType-meta.xml file to appear in the Report Builder.
What does the deployed boolean field control on a ReportType?
When deployed is false (In Development), only administrators and users with Manage Custom Report Types can see and run reports built on that ReportType. Set deployed to true to make it available to end users.
Comments
Questions, corrections and tips are welcome. Comments are GitHub Discussions, so you sign in with GitHub.