Overview

The CustomObject metadata type is the foundational data model building block on the Salesforce platform. Despite its name, CustomObject represents six distinct entity flavors in Metadata API: custom objects (__c), standard object customizations (Account, Case), external objects (__x), big objects (__b), platform events (__e), and custom metadata types (__mdt).

Use CustomObject metadata whenever you provision database tables, configure Organization-Wide Default (OWD) sharing models (sharingModel), enable field history tracking (enableHistory), or set up standard Name field numbering (nameField). Do not treat the raw .object XML as a monolithic file in modern repositories—always use Salesforce DX source decomposition so multiple developers can work on the same object without Git merge conflicts.

CustomObject Metadata Type Standard Objects (Account, Case) Custom Objects (Invoice__c) Custom Metadata Types (TaxRate__mdt) Platform Events (OrderEvent__e) External Objects (ERP_Order__x) Big Objects (AuditLog__b) Decomposed SFDX Source Folder Invoice__c.object-meta.xml fields/*.field-meta.xml validationRules/*.validationRule-meta.xml recordTypes/*.recordType-meta.xml

As shown in the diagram above, CustomObject acts as the parent container across six entity suffixes and decomposes into modular subdirectories (fields/, validationRules/, recordTypes/, listViews/, compactLayouts/, fieldSets/, businessProcesses/, and webLinks/) in Salesforce DX projects.

Prerequisites

  • Permissions: “Customize Application” and “Modify Metadata Through Metadata API Functions” permissions in the target org.
  • CLI Version: Salesforce CLI (sf) v2.x or later with source tracking enabled.
  • API Version: Available since API version 10.0 (__x external objects since v32.0). Verified against API version 68.0 (Winter ’27).

Source Format vs Metadata Format Architecture

Salesforce DX transforms the legacy monolithic .object file into a decomposed folder hierarchy inside force-app/main/default/objects/<ObjectName>/. This decomposition is critical for enterprise CI/CD pipelines.

Property Classic Metadata API Format SFDX Source Format (Recommended)
Root File Path objects/Invoice__c.object objects/Invoice__c/Invoice__c.object-meta.xml
Custom Fields Embedded <fields> inside .object objects/Invoice__c/fields/<Field>__c.field-meta.xml
Validation Rules Embedded <validationRules> objects/Invoice__c/validationRules/<Rule>.validationRule-meta.xml
Record Types Embedded <recordTypes> objects/Invoice__c/recordTypes/<RT>.recordType-meta.xml
List Views Embedded <listViews> objects/Invoice__c/listViews/<View>.listView-meta.xml

Tip

When deploying partial changes in CI/CD, deploying Invoice__c.object-meta.xml updates only the object-level properties (such as sharingModel or enableReports) without touching fields or validation rules.

Key XML Fields and Architect Configuration

The root CustomObject XML file controls database behavior, security boundaries, and platform feature indexing.

Field Name Type Architectural Purpose & Best Practice
deploymentStatus DeploymentStatus Required for custom objects (Deployed or InDevelopment). Always set to Deployed in version control.
sharingModel SharingModel Internal OWD (Private, Read, ReadWrite, ControlledByParent). Set ControlledByParent when a Master-Detail field exists.
externalSharingModel SharingModel Experience Cloud OWD. Must be equal to or more restrictive than sharingModel.
nameField CustomField Defines the standard Name field (Text or AutoNumber with a displayFormat like INV-{00000}).
enableHistory boolean Enables tracking up to 20 fields in the companion *__History table (up to 60 with Field Audit Trail).
enableReports boolean Allows custom report types and standard reports on the object.
enableActivities boolean Allows Task and Event records to link to this object via the WhatId polymorphic lookup.
enableBulkApi / enableStreamingApi boolean Both must be true together with enableSharing to support Bulk API 2.0 and Streaming API / PushTopics.

Querying CustomObject via SOQL and Tooling API

You can inspect CustomObject metadata at runtime using standard SOQL against EntityDefinition or via the Tooling API CustomObject table. These queries are invaluable for automated schema audits, security reviews, and dynamic Apex frameworks.

1. Audit Organization-Wide Defaults (OWD) Across Custom Objects

Use standard SOQL on EntityDefinition to verify internal and external sharing models across all custom objects:

SOQL
SELECT QualifiedApiName, Label, InternalSharingModel, ExternalSharingModel,
       IsCustomizable, IsEverCreatable, KeyPrefix
FROM EntityDefinition
WHERE IsCustomSetting = false
  AND QualifiedApiName LIKE '%__c'
ORDER BY QualifiedApiName ASC
LIMIT 200

2. Query CustomObject Metadata Directly via Tooling API

Execute a Tooling API query (sf data query --use-tooling-api) to find custom objects created or modified recently, along with their namespace and description:

SOQL
SELECT Id, DeveloperName, NamespacePrefix, Description,
       SharingModel, ExternalSharingModel, ManageableState, CreatedDate
FROM CustomObject
WHERE NamespacePrefix = null
ORDER BY CreatedDate DESC

3. Count Custom Fields per Object Using Tooling API

Identify objects approaching the custom field governor limit (500–800 fields depending on edition):

SOQL
SELECT TableEnumOrId, COUNT(Id) totalCustomFields
FROM CustomField
GROUP BY TableEnumOrId
ORDER BY COUNT(Id) DESC

Example

The following SFDX source file defines an enterprise Invoice__c custom object with private internal and external sharing, auto-number record naming, field history tracking, and activity support.

force-app/main/default/objects/Invoice__c/Invoice__c.object-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata">
    <actionOverrides>
        <actionName>View</actionName>
        <comment>Action override updated by Lightning App Builder during activation.</comment>
        <content>Invoice_Record_Page</content>
        <formFactor>Large</formFactor>
        <skipRecordTypeSelect>false</skipRecordTypeSelect>
        <type>Flexipage</type>
    </actionOverrides>
    <allowInChatterGroups>false</allowInChatterGroups>
    <compactLayoutAssignment>Invoice_Highlight_Compact_Layout</compactLayoutAssignment>
    <deploymentStatus>Deployed</deploymentStatus>
    <description>Stores customer billing invoices synchronized with the ERP general ledger.</description>
    <enableActivities>true</enableActivities>
    <enableBulkApi>true</enableBulkApi>
    <enableFeeds>true</enableFeeds>
    <enableHistory>true</enableHistory>
    <enableLicensing>false</enableLicensing>
    <enableReports>true</enableReports>
    <enableSearch>true</enableSearch>
    <enableSharing>true</enableSharing>
    <enableStreamingApi>true</enableStreamingApi>
    <externalSharingModel>Private</externalSharingModel>
    <label>Invoice</label>
    <nameField>
        <displayFormat>INV-{YYYY}-{000000}</displayFormat>
        <label>Invoice Number</label>
        <trackFeedHistory>false</trackFeedHistory>
        <trackHistory>false</trackHistory>
        <type>AutoNumber</type>
    </nameField>
    <pluralLabel>Invoices</pluralLabel>
    <searchLayouts>
        <customTabListAdditionalFields>Account__c</customTabListAdditionalFields>
        <customTabListAdditionalFields>Invoice_Date__c</customTabListAdditionalFields>
        <customTabListAdditionalFields>Total_Amount__c</customTabListAdditionalFields>
        <customTabListAdditionalFields>Status__c</customTabListAdditionalFields>
        <searchResultsAdditionalFields>Account__c</searchResultsAdditionalFields>
        <searchResultsAdditionalFields>Total_Amount__c</searchResultsAdditionalFields>
        <searchResultsAdditionalFields>Status__c</searchResultsAdditionalFields>
    </searchLayouts>
    <sharingModel>Private</sharingModel>
    <visibility>Public</visibility>
</CustomObject>

Package Manifest (package.xml) Gotcha with Wildcards

When retrieving objects with package.xml, <members>*</members> only matches custom objects. Standard objects (Account, Contact, Opportunity) must be named explicitly:

manifest/package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <!-- Wildcard retrieves all custom objects (__c, __mdt, __e, __b, __x) -->
        <members>*</members>
        <!-- Standard objects must be listed explicitly -->
        <members>Account</members>
        <members>Opportunity</members>
        <members>Invoice__c</members>
        <name>CustomObject</name>
    </types>
    <version>68.0</version>
</Package>

Deploy or retrieve the Invoice__c custom object using the sf CLI:

Terminal
# Retrieve a specific custom object and all its decomposed child metadata
sf project retrieve start --metadata CustomObject:Invoice__c

# Deploy only the object-meta.xml properties without redeploying fields
sf project deploy start --source-dir force-app/main/default/objects/Invoice__c/Invoice__c.object-meta.xml

Limits and Gotchas

  • Custom Object Limit per Org: Up to 3,000 custom objects per org in Unlimited and Performance Editions (2,000 base + installed packages), and 200 in Enterprise Edition.
  • Sharing Recalculation Locks: Changing sharingModel (for example, from ReadWrite to Private or ControlledByParent) triggers an asynchronous Organization-Wide Default sharing recalculation in large orgs. Concurrent deployments touching the same object’s sharing rules will fail with an UNABLE_TO_LOCK_ROW or SHARING_RECALCULATION_IN_PROGRESS error.
  • Master-Detail OWD Requirement: If an object is on the detail side of a CustomField Master-Detail relationship, its sharingModel must be set to ControlledByParent, or the deployment fails.
  • Big Object Indexes: Big Objects (__b) require an <indexes> element defined at creation time; you cannot modify or add an <indexes> block to an existing Big Object after deployment.
  • Profile & PermissionSet Side Effect: Retrieving a CustomObject alongside Profile or PermissionSet in the same manifest automatically populates <objectPermissions>, <fieldPermissions>, and <layoutAssignments> for that object inside the retrieved security files.

Warning

Never delete a <fields> node from a monolithic .object file expecting it to delete the field in the target org. In Metadata API, omitting a field simply leaves it untouched in the target org; you must use a destructiveChanges.xml manifest or sf project delete source to remove a field or object.

Frequently asked questions

What is the difference between SFDX source format and classic Metadata API format for CustomObject?

Classic Metadata API stores the entire object, fields, validation rules, and list views in a single monolithic .object file. SFDX source format decomposes these into separate folders under objects/ObjectName/ with an .object-meta.xml root file, preventing Git merge conflicts.

Can I deploy a CustomObject without deploying its custom fields?

Yes, in SFDX source format you can deploy only the MyObject__c.object-meta.xml file to update object-level properties like sharingModel or label without deploying the fields/ directory.

Why are my newly deployed custom fields invisible to users?

Deploying a CustomObject or CustomField does not automatically grant Field-Level Security (FLS) or Object CRUD permissions unless you deploy the corresponding PermissionSet or Profile metadata in the same transaction.

How do I query CustomObject metadata using SOQL?

Query the EntityDefinition standard object in SOQL or Tooling API, or query the CustomObject table directly via Tooling API to inspect DeveloperName, NamespacePrefix, SharingModel, and ManageableState.

Does the wildcard character in package.xml retrieve standard objects under CustomObject?

No. Using an asterisk (*) with CustomObject in package.xml retrieves only custom objects. To retrieve standard objects such as Account or Opportunity, you must explicitly list each standard object name in a members tag.

What is the difference between sharingModel and externalSharingModel on CustomObject?

The sharingModel field sets Organization-Wide Defaults (OWD) for internal users, while externalSharingModel sets OWD for Experience Cloud external users. External access cannot be more permissive than internal access.

Comments

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