Platform Custom Report Type Generate
Use to create, generate, or validate Salesforce Custom Report Type metadata. Trigger on custom report types, CRTs, cross-object reports, report builder data sources, exposing fields for reporting across related objects, inner vs outer joins in reports, report type categories, or deployment errors for .reportType-meta.xml files. Do NOT trigger for running, editing, or filtering existing reports; creating report folders, dashboards, or list views; or non-authoring reporting questions.
- Skill ID
- forcedotcom/sf-skills/platform-custom-report-type-generate
- Publisher
- forcedotcom
- Repository
- sf-skills
- Installs
- 0
- Files
- 6
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
forcedotcom/sf-skills/platform-custom-report-type-generateInstalls these files- SKILL.md
- examples/AccountProjectsWithTasks.reportType-meta.xml
- examples/AccountsWithIndustry.reportType-meta.xml
- examples/AccountsWithProjects.reportType-meta.xml
- references/category-values.md
- references/errors-and-troubleshooting.md
What this skill tells the agent
Specification
Salesforce Custom Report Type Metadata Knowledge
Overview
Custom Report Types (CRTs) define the data framework for Salesforce reports. They specify a primary object, up to 3 related objects, the relationship (join) between them, and which fields are available in the report builder.
Purpose
- Enable reporting across custom objects and custom relationships not covered by standard report types
- Curate a focused set of fields for report builders (including fields reached via lookup)
- Control inner/outer join behavior to include or exclude primary records without related records
Configuration
File extension: .reportType-meta.xml. The file basename is the report type's developer name (e.g. AccountsWithProjects.reportType-meta.xml). Each CRT is a single file, not nested under an object folder.
Key Elements
Top-level <ReportType> children:
| Element | Required | Notes |
|---|---|---|
<fullName> | Yes | API identifier; must match the file name. Letters, numbers, underscores; must begin with a letter; no spaces; no trailing underscore; no consecutive underscores |
<label> | Yes | Human-friendly name shown in the report type picker |
<description> | Recommended | State the business "why" — who uses this and what they learn |
<baseObject> | Yes | API name of the primary object (e.g. Account, Project__c). Cannot be changed after initial creation. All objects, including custom and external, are supported (external objects from API 38.0+) |
<category> | Recommended | Report builder category — see references/category-values.md |
<deployed> | Yes | true to expose to users; false while building/iterating |
<join> | Conditional | Adds a related object and its join behavior. Nest further <join> blocks for deeper relationships |
<sections> | Recommended | Groups of columns available to the report type. Though not strictly required, a report without columns isn't useful |
<sections> (group of columns) sub-elements:
| Element | Required | Notes |
|---|---|---|
<masterLabel> | Yes | Section heading shown in the report builder |
<columns> | Conditional | One per field exposed in the section |
<columns> (single field) sub-elements:
| Element | Required | Notes |
|---|---|---|
<field> | Yes | Field API name (or dotted lookup-traversal path) |
<table> | Yes | The object the field belongs to — base object name or dotted relationship path |
<checkedByDefault> | Yes | true if the column is selected by default in the report builder |
<displayNameOverride> | No | Custom column label shown in the report builder, overriding the field's default label |
Critical Rules (Read First)
Rule 1: If <fullName> Is Present, It Must Match the File Name
In source format, fullName is inherited from Metadata and derived from the file name, so the <fullName> element is technically optional. The repo convention is to include it. If you include `<fullName>`, its value must equal the file name (everything before `.reportType-meta.xml`) exactly — same characters, same casing, same underscores.
Wrong — file name and <fullName> differ:
- File:
account_projects.reportType-meta.xml <fullName>AccountProjects</fullName>(Mismatch: file usesaccount_projects, fullName usesAccountProjects)
Right — file name and <fullName> are identical:
- File:
AccountProjects.reportType-meta.xml <fullName>AccountProjects</fullName>
Rule 2: Join Semantics — outerJoin Controls Inclusion
Each <join> block has an <outerJoin> element that determines which primary records appear in the report:
<outerJoin> value | Behavior | Report Builder Label |
|---|---|---|
false | Inner join — only primary records that HAVE at least one related record | "Each 'A' record must have at least one related 'B' record" |
true | Outer join — all primary records, with or without related records | "'A' records may or may not have related 'B' records" |
Default when unspecified: Use true (outer join) when the user wants to see all primary records regardless of children. Use false when the report only makes sense if children exist.
Rule 3: Each Object Needs Its Own <sections> Block
Every object in the CRT (primary + each joined object) must have a corresponding <sections> block that lists the fields exposed for reporting. Without a section for an object, none of its fields appear in the report builder.
<masterLabel>on each section is the section heading in the report builder<columns>entries list the fields — each with a<field>(API name) and<table>(object API name)- For fields reached via lookup, use the relationship path in
<field>(e.g.Owner.Namewith<table>set to the owning object)
Rule 4: Field API Names, Not Labels
Use exact API names for fields: standard fields use their defined names (Name, CreatedDate, OwnerId), custom fields use Field__c. Custom objects must include __c.
Wrong:
<field>Account Name</field>
Right:
<field>Name</field>with<table>Account</table>
Rule 5: Relationship Path for Joined Objects
When adding a <join>, the <relationship> element must use the child relationship name as defined on the lookup/master-detail field pointing from the child object to the parent. For custom relationships, this typically ends in __r.
Wrong:
<relationship>Project</relationship>(for a custom child relationship)
Right:
<relationship>Projects__r</relationship>(child relationship name)<relationship>Contacts</relationship>(standard, non-custom child relationship)
Rule 6: Maximum 4 Objects Total in a Join Chain
A single CRT can join a maximum of four objects total (the base object + up to 3 additional objects via nested <join> blocks).
Rule 7: No Inner Join After an Outer Join
Once the join chain contains an outer join (<outerJoin>true</outerJoin>), every subsequent nested join must also be an outer join. An inner join that follows an outer join earlier in the sequence is not allowed.
Wrong:
<join>
<outerJoin>true</outerJoin> <!-- outer join first -->
<relationship>Contacts</relationship>
<join>
<outerJoin>false</outerJoin> <!-- WRONG: inner join after outer -->
<relationship>Assets</relationship>
</join>
</join>Right:
<join>
<outerJoin>true</outerJoin>
<relationship>Contacts</relationship>
<join>
<outerJoin>true</outerJoin> <!-- outer stays outer -->
<relationship>Assets</relationship>
</join>
</join>Rule 8: <table> for Joined Objects Uses Dotted Path
In <sections>, the <table> element identifies which object in the join chain each column belongs to. For the base object, use the object name directly (e.g. Account). For joined objects, use the dotted relationship path from the base object.
| Object in chain | <table> value |
|---|---|
| Base (Account) | Account |
| First join (Account → Contacts) | Account.Contacts |
| Nested join (Account → Contacts → Assets) | Account.Contacts.Assets |
Rule 9: Field Paths Can Traverse Lookups
<field> values may reference fields reached via lookup relationships using dot notation — for example Owner.Email (owner User's email) or ReportsTo.CreatedBy.Contact.Owner.MobilePhone. The <table> must still be the object that owns the starting field.
Rule 10: Historical Trending Fields Use _hst Suffix
For a field with trackTrending=true, the API name in <field> and <table> uses the _hst suffix:
<columns>
<checkedByDefault>false</checkedByDefault>
<field>Field2__c_hst</field>
<table>CustomTrendedObject__c.CustomTrendedObject__c_hst</table>
</columns>More from forcedotcom
Use this skill in a project
Open any RiverX project, search the Skills panel for forcedotcom/sf-skills/platform-custom-report-type-generate, and install it in one click.
