This article is part of the Salesforce Admin Git & sf CLI series, a complete guide to version control and metadata management for Salesforce admins. Articles are standalone; no need to read in order.
TL;DR
- Retrieve all custom objects:
sf project retrieve start --metadata CustomObject --target-org myorg: pulls every CustomObject in the org intoforce-app/main/default/objects/ - Retrieve one specific component:
sf project retrieve start --metadata "CustomObject:Account" --target-org myorg: the colon separates the type name from the component name - List before you retrieve:
sf org list metadata --metadata-type CustomObject --target-org myorg: shows every component's exact API name, which you need for the colon syntax - Source format vs MDAPI format: source format (the default) writes one file per field, one file per record type. Much cleaner for git diffs. MDAPI format dumps one big XML file per object.
What You'll Learn
- How to retrieve CustomObject, CustomField, Layout, FlexiPage, RecordType, and CompactLayout using sf CLI
- How to list what components are in your org before retrieving
- The difference between source format and MDAPI format
- How to retrieve packaged vs unpackaged metadata
- How to use package.xml to retrieve multiple types at once
- What the retrieved files look like on disk
The Problem
Your Salesforce org has dozens of custom objects, fields, and page layouts. You want to save them to version control. Where do you start?
You know you need to use sf CLI. You know there's a retrieve command somewhere. But what you're missing is the exact syntax, the right metadata type names, and an understanding of what actually lands on your hard drive after you run the command.
This article walks through all of it. By the end you'll be able to pull CustomObject, CustomField, Layout, FlexiPage, RecordType, and CompactLayout metadata out of your org and into files you can commit to git.
Common questions this article answers:
- How do I retrieve a CustomObject with sf CLI?
- How do I get just one field rather than the whole object?
- What does
force-app/main/default/objects/look like after retrieval? - How do I retrieve Layout or FlexiPage metadata?
- What is the difference between source format and MDAPI format?
Quick Answer
Three commands cover most situations:
# 1. List what's in your org (get the exact API names)
sf org list metadata --metadata-type CustomObject --target-org myorg
# 2. Retrieve all custom objects
sf project retrieve start --metadata CustomObject --target-org myorg
# 3. Retrieve one specific object by name
sf project retrieve start --metadata "CustomObject:MyObject__c" --target-org myorg
The --target-org flag takes the alias you set when you connected your org (Article 3 of this series covers that). If you only have one org connected you may be able to omit it, but it is safer to always include it.
These commands work identically on Windows and macOS. sf CLI handles the path differences for you.
Section 1: List Available Components Before Retrieving
Before you retrieve anything, run a list command. This gives you two things: confirmation that the metadata type exists in your org, and the exact API names you need for the colon syntax in retrieve commands.
# List all custom objects in the org
sf org list metadata --metadata-type CustomObject --target-org myorg
# List all custom fields (across all objects)
sf org list metadata --metadata-type CustomField --target-org myorg
# List all layouts
sf org list metadata --metadata-type Layout --target-org myorg
# List FlexiPages (Lightning App Builder pages)
sf org list metadata --metadata-type FlexiPage --target-org myorg
# See every metadata type available in the org
sf org list metadata-types --target-org myorg
The output looks something like this for CustomObject:
=== CustomObjects
FULL NAME NAMESPACE PREFIX TYPE
───────────────────── ──────────────── ────────────
Account CustomObject
Contact CustomObject
MyObject__c CustomObject
AnotherObject__c CustomObject
The FULL NAME column is what matters. That value (MyObject__c, Account, etc.) is exactly what you pass after the colon in a retrieve command.
A few things worth knowing about the naming patterns:
- CustomObject: The full name is the object API name:
MyObject__cfor custom objects,Accountfor standard objects that have been customised. - CustomField: The full name is
ObjectName__c.FieldName__c, the object and field joined with a dot. For example:MyObject__c.Status__c. - Layout: The full name is
ObjectName-Layout Name, object and layout name joined with a hyphen. For example:Account-Account Layout. - FlexiPage: The full name is just the page API name:
MyHomePage,My_Record_Page, etc.
Knowing these patterns means you can often guess the right full name without running the list command first. When in doubt, list first.
Section 2: Retrieve in Source Format (Default, Recommended)
Source format is the default output format for sf project retrieve start. Files land in force-app/main/default/ in a structured folder tree with one file per sub-component. This is the format you want for version control: git diffs are clean and readable.
# Retrieve all custom objects (every CustomObject in the org)
sf project retrieve start --metadata CustomObject --target-org myorg
# Retrieve a specific object
sf project retrieve start --metadata "CustomObject:Account" --target-org myorg
# Retrieve a specific custom field
sf project retrieve start --metadata "CustomField:MyObject__c.MyField__c" --target-org myorg
# Retrieve all Layout metadata
sf project retrieve start --metadata Layout --target-org myorg
# Retrieve a specific layout
sf project retrieve start --metadata "Layout:Account-Account Layout" --target-org myorg
# Retrieve all FlexiPages (Lightning App Builder pages)
sf project retrieve start --metadata FlexiPage --target-org myorg
# Retrieve a specific FlexiPage
sf project retrieve start --metadata "FlexiPage:MyPage" --target-org myorg
# Retrieve all RecordType metadata
sf project retrieve start --metadata RecordType --target-org myorg
# Retrieve all CompactLayout metadata
sf project retrieve start --metadata CompactLayout --target-org myorg
# Retrieve multiple types in one command
sf project retrieve start --metadata CustomObject CustomField Layout FlexiPage --target-org myorg
The pattern is consistent throughout:
--metadata TypeNameretrieves all components of that type.--metadata "TypeName:ComponentName"retrieves one specific component. The colon:separates the type name from the component's full name.- Multiple types are space-separated:
--metadata CustomObject CustomField Layout.
When a retrieve completes, sf CLI prints a summary of what was written to disk. If you see zero files written and no error, it usually means the org has no components of that type. Run the list command to confirm.
Section 3: Retrieve in MDAPI Format
MDAPI format produces the older Metadata API structure: a flat folder of files plus a package.xml manifest. You rarely need this for day-to-day version control work, but it is useful if a downstream tool (a legacy CI pipeline, a third-party deployment tool) expects the traditional format.
# Retrieve CustomObject in MDAPI format
sf project retrieve start --metadata CustomObject --target-org myorg --target-metadata-dir mdapi-output --unzip
# Retrieve Layout in MDAPI format
sf project retrieve start --metadata Layout --target-org myorg --target-metadata-dir mdapi-output --unzip
The --target-metadata-dir flag tells sf CLI to produce a ZIP file containing the MDAPI format structure in the specified folder. The --unzip flag auto-extracts the ZIP so you get the raw files immediately. The folder is created if it does not exist.
Note: --output-dir is a different flag. It changes where source format files land. It does NOT produce MDAPI format.
Source format vs MDAPI format: the practical difference:
| Source Format | MDAPI Format | |
|---|---|---|
| Output location | force-app/main/default/ |
Specified by --target-metadata-dir |
| Object fields | One .field-meta.xml file per field |
All fields inside one MyObject__c.object file |
| Record types | One .recordType-meta.xml per record type |
All record types inside the .object file |
| Git diffs | Clean: one change = one changed file | Noisy: any change touches the whole .object file |
| Tool compatibility | Salesforce DX tools, VS Code extension | Legacy CI tools, older Ant-based deployments |
For version control purposes, source format wins every time. MDAPI format was the standard before Salesforce DX. It is still valid and still deployed, but source format is what you want in git.
Section 4: Retrieve Using package.xml (Manifest-Based)
If you need to retrieve multiple types repeatedly (for example, every time you sync a sandbox) the cleanest approach is a package.xml manifest file that lists everything you want. Then you run one command and it retrieves the lot.
# Retrieve everything specified in package.xml
sf project retrieve start --manifest package.xml --target-org myorg
Here is a complete package.xml for custom configuration metadata:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>*</members>
<name>CustomObject</name>
</types>
<types>
<members>*</members>
<name>CustomField</name>
</types>
<types>
<members>*</members>
<name>Layout</name>
</types>
<types>
<members>*</members>
<name>FlexiPage</name>
</types>
<types>
<members>*</members>
<name>RecordType</name>
</types>
<types>
<members>*</members>
<name>CompactLayout</name>
</types>
<version>62.0</version>
</Package>
Save this as package.xml in the root of your project (next to sfdx-project.json), then run the retrieve command above.
A few things to know about package.xml:
<members>*</members>means "all components of this type." The wildcard*retrieves everything.- To retrieve just one component, replace
*with the full name:<members>Account</members>retrieves only the Account object. - The
<version>tag should match your org's API version. Checksfdx-project.jsonfor thesourceApiVersionvalue: that is the version sf CLI is using. - You can mix wildcards and specific names in the same
<types>block by adding multiple<members>tags.
package.xml is particularly useful when you hand off the project to a teammate or set up a CI job. Anyone who runs sf project retrieve start --manifest package.xml --target-org myorg gets exactly the same set of metadata, every time.
Section 5: Retrieve Packaged Metadata
If your org has installed packages (managed or unmanaged), those components also live in the org and can be retrieved.
# Retrieve using a package.xml that references packaged components
sf project retrieve start --manifest package.xml --target-org myorg
To retrieve components from a specific package, reference them by their full name including the namespace prefix in your package.xml:
<types>
<members>mypkg__MyObject__c</members>
<name>CustomObject</name>
</types>
Important caveat about managed packages: Managed package metadata (components with a namespace prefix like mypkg__MyObject__c) is read-only. You can retrieve it to inspect it, read the field definitions, understand how it is structured. But you cannot modify it and deploy it back. Salesforce will reject any attempt to overwrite managed package metadata.
The practical rule: retrieve managed package metadata only when you need to understand it. Never commit it to your version-controlled source directory as if it were your own configuration. If it accidentally ends up in your repo, remove it. It will cause confusion and deployment failures.
Section 6: Preview Before Retrieving
Before running a large retrieve, you can see exactly what would happen without actually writing any files:
# See what WOULD be retrieved without actually retrieving it
sf project retrieve preview --target-org myorg
The preview command shows which files would be added, updated, or deleted locally compared to what is in the org. This is useful when you are about to run a broad retrieve (like all CustomObjects) and want to make sure you are not about to overwrite local changes you have not committed yet.
Think of it as a dry run: same logic, no file writes.
Section 7: What the Files Look Like on Disk
After retrieving in source format, your project folder looks like this:
force-app/
└── main/
└── default/
├── objects/
│ ├── Account/
│ │ ├── Account.object-meta.xml ← object definition
│ │ ├── fields/
│ │ │ ├── MyField__c.field-meta.xml ← one file per field
│ │ │ └── AnotherField__c.field-meta.xml
│ │ ├── recordTypes/
│ │ │ └── MyRecordType.recordType-meta.xml
│ │ ├── compactLayouts/
│ │ │ └── MyCompactLayout.compactLayout-meta.xml
│ │ └── listViews/
│ │ └── All.listView-meta.xml
├── layouts/
│ └── Account-Account Layout.layout-meta.xml
└── flexipages/
└── MyPage.flexipage-meta.xml
Each sub-component gets its own file. That is the whole point of source format: when someone changes a field, only that field's file changes in git. The diff shows exactly what the label was before and what it is now, which validation rules changed, whether the field became required. None of that is visible in MDAPI format where one field change means a diff across a 1,000-line XML blob.
A few details about the structure:
Account.object-meta.xmlcontains the object-level settings: API name, label, sharing model, search layouts. It does not contain the fields.- Each field lives in
fields/FieldName__c.field-meta.xml: the field's type, label, length, picklist values, validation, everything. - Record types, compact layouts, and list views each get their own subfolder following the same pattern.
- Layouts live in
layouts/at the top level, not inside the object folder. They are a separate metadata type. - FlexiPages live in
flexipages/: one file per App Builder page.
Troubleshooting
"No such metadata type: CustomObject"
The type name is case-sensitive in the Metadata API. CustomObject is correct; customobject or Custom Object will fail. Run sf org list metadata-types --target-org myorg to see the exact names your org supports.
"Component not found"
The component name after the colon must match exactly what sf org list metadata returned, including capitalisation, namespace prefix, and any dots or hyphens. Run the list command first and copy the full name directly from the output.
Retrieve succeeds but produces no files
This happens when the org has no components of that type, or your project's ignore rules are filtering the output. Check with sf org list metadata --metadata-type <Type> --target-org myorg first to confirm components exist.
--target-metadata-dir creates an empty folder
Same cause: no components matched. Confirm with the list command before retrieving.
Managed package fields retrieved but a read-only warning appears This is expected behaviour. Salesforce flags managed metadata as read-only during retrieve. Retrieve it to inspect it if you need to, but do not commit it as your own source. It cannot be deployed.
Frequently Asked Questions
How do I retrieve just one custom field, not the whole object?
Use --metadata "CustomField:ObjectName__c.FieldName__c". The API name of a custom field always includes the object name, dot-separated. Get the exact name from sf org list metadata --metadata-type CustomField --target-org myorg and copy it directly from the output.
What Salesforce API version does sf CLI use?
sf CLI uses the API version set in your sfdx-project.json file. Look for the sourceApiVersion key. The package.xml example above uses 62.0; update it to match your org's version. If you are unsure what version your org is on, check Setup → Company Information → Salesforce.com Organization ID page or run sf org display --target-org myorg and check the instance URL.
Should I commit everything that gets retrieved?
No. Retrieve is a read operation: it pulls everything Salesforce decides to return for the types you specified. Only commit files that represent your team's customisations. If you retrieved all CustomObjects to see what is there, stage only the objects your team owns. Leave standard objects and managed package objects out of your committed source unless you have a specific reason to track them.
What is the difference between CustomObject and CustomField in the Metadata API?
They are separate metadata types. Retrieving CustomObject gives you the object shell (API name, label, sharing model, search layouts) but not the fields. Retrieving CustomField gives you the fields. To get a complete picture of an object, include both types in your retrieve command or package.xml.
What about standard fields: can I retrieve those?
Standard fields (like Account.Name, Account.Phone) are not retrievable as metadata. They are part of Salesforce core and are not exposed through the Metadata API as independent components. Only custom fields with the __c suffix are retrievable.
Key Takeaways
- List first: Run
sf org list metadata --metadata-type <Type> --target-org myorgbefore retrieving to get exact component API names. You need these for the colon syntax. - Source format is the default:
sf project retrieve startwrites granular files toforce-app/main/default/, better for git diffs than MDAPI format. - MDAPI format when needed: Add
--target-metadata-dir folder --unzipto get the flat package.xml + single-file-per-object structure for legacy tooling. - Type:Name syntax:
--metadata "CustomObject:Account"retrieves one component;--metadata CustomObjectretrieves all components of that type. - package.xml for repeatable retrieves:
--manifest package.xmlretrieves everything listed in the manifest. The right approach for CI pipelines and regular syncs. - Managed package metadata is read-only: Retrieve it to inspect, never commit it as your own source.
What's Next?
This is Article 4 of the Salesforce Admin Git & sf CLI series.
Recommended Reading:
- Article 5: Fetching Automation Metadata: Flow, WorkflowRule, ApprovalProcess
- Article 6: Fetching Security Metadata: Profile, PermissionSet, Role
- Article 10: Git Basics for Salesforce Admins: commit your retrieved metadata to version control
- Article 11: Deploying Metadata Back to Salesforce: push changes back to an org
Action Items:
- Run
sf org list metadata --metadata-type CustomObject --target-org myorgto see what objects are in your org - Retrieve one object:
sf project retrieve start --metadata "CustomObject:YourObject__c" --target-org myorg - Open the retrieved files in VS Code and inspect the XML
Resources & References
- Salesforce Metadata API Developer Guide: CustomObject
- sf CLI Reference: sf project retrieve start
- Salesforce Metadata Coverage Report
About This Guide: Part of the Salesforce Admin Git & sf CLI series. Each article stands alone.
Tags: #salesforce #sfcli #metadata #versioncontrol #salesforceadmin