API Version Management
This skill activates when you need to audit, standardize, or upgrade the API versions declared across a Salesforce metadata codebase. Every Salesforce metadata component carries an API version that controls which platform behaviors, fields, and features are available at runtime. Version drift — where different components run on different API versions — causes subtle behavioral inconsistencies, test failures, and eventual breakage when Salesforce retires old versions.
Before Starting
Gather this context before working on anything in this domain:
- What is the project's
sourceApiVersion? Found insfdx-project.json, this is the baseline version the project intends to use. Individual components can override it, and that override is where drift begins. - What is the current Salesforce release? Each major release (Spring, Summer, Winter) increments the API version by 1. Anchor points: Spring '24 = 60.0, Summer '24 = 61.0, Winter '25 = 62.0, Spring '25 = 63.0, Summer '25 = 64.0, Winter '26 = 65.0, Spring '26 = 66.0, Summer '26 = 67.0. Salesforce retires versions on a rolling basis in distinct waves — 7.0 through 20.0 were retired in Summer '22; 21.0 through 30.0 were deprecated in Summer '22 and retired in Summer '25, consistent with the minimum 3-year deprecation notice policy. Deprecated and retired are not the same state: a deprecated version still works.
- Are there integrations using explicit API version numbers? External systems calling
/services/data/vXX.0/or/services/Soap/c/XX.0endpoints pin to a version. These must be inventoried alongside metadata.
Core Concepts
1. The Three Layers of API Versioning
Salesforce API versions operate at three distinct layers, each independently configurable:
- Transport API version — the version in REST/SOAP endpoint URLs used by external integrations (e.g.,
/services/data/v63.0/). This controls which API resources and fields are visible to the caller. sourceApiVersioninsfdx-project.json— the default version used by the Salesforce CLI duringsf project deployandsf project retrieve. It sets the baseline for metadata operations but does not override per-component versions at runtime.- Per-component
apiVersion— declared in each component's metadata file (.cls-meta.xml,.js-meta.xml,.trigger-meta.xml,.cmp). This is the version the platform actually uses when executing the component. An Apex class at version 50.0 sees different System method signatures than one at version 63.0.
Version drift occurs when these three layers diverge. The most dangerous drift is between sourceApiVersion and per-component versions, because developers assume they are deploying at one version while the platform executes at another.
2. Salesforce API Retirement Policy
Salesforce publishes an API End-of-Life policy with a minimum 3-year deprecation notice. When a version is retired:
- REST and SOAP calls to that version return an error.
- Metadata components pinned to that version may exhibit undefined behavior or deployment failures.
ApiTotalUsageevent logs in Event Monitoring track which versions are actively called, providing a detection mechanism before retirement hits.
Two distinct waves have completed, and conflating them is the most common error in this area:
| Versions | Deprecated | Retired |
|---|---|---|
| 7.0 – 20.0 | before Summer '22 | Summer '22 |
| 21.0 – 30.0 | Summer '22 | Summer '25 |
So the "3-year notice" is visible in the second row: deprecation in Summer '22, retirement three years later in Summer '25. As of Summer '26 the 21.0–30.0 wave has already landed — a team planning against "the next wave" using the Summer '22 date is reasoning with three-year-stale runway. Calls to a retired version fail hard: REST returns 410 GONE, SOAP returns 500 UNSUPPORTED_API_VERSION, Bulk returns 400 InvalidVersion. Proactive scanning is essential because Salesforce does not automatically upgrade component versions.
3. LWC Component Versioning (Spring '25+)
Starting in Spring '25, Lightning Web Components require an explicit apiVersion in their .js-meta.xml file. Previously, LWC inherited the org's current version implicitly. This change means:
- New LWC bundles must declare
<apiVersion>63.0</apiVersion>(or current). - Existing LWC bundles without an explicit version will use the org default, but this implicit behavior is deprecated.
- The declared version controls which base components, wire adapters, and decorators are available.
4. ApiTotalUsage Event Logs
The ApiTotalUsage event type in Event Monitoring records every API call with the version used. Query these logs to find external integrations still calling deprecated versions:
SELECT ApiVersion, Client, Count
FROM ApiTotalUsage
WHERE ApiVersion < 31
ORDER BY Count DESC
This data is critical for building an upgrade plan that covers runtime usage, not just static metadata.
Common Patterns
Pattern 1: Full Codebase Version Audit
When to use: Before a major Salesforce release, after inheriting a project, or when deprecation notices arrive.
How it works:
- Read
sourceApiVersionfromsfdx-project.json. - Scan all
*-meta.xmlfiles for<apiVersion>elements. - Scan
.cmpand.appAura files for<aura:component>or<aura:application>version attributes. - Compare each component's version against
sourceApiVersionand flag drift. - Flag any component below the minimum safe version (currently 31.0).
Why not the alternative: Manual spot-checking misses components because a typical org has hundreds of versioned files scattered across classes, triggers, pages, components, and flows.
Pattern 2: Incremental Version Pinning
When to use: When upgrading all components at once is too risky (large codebase, limited test coverage).
How it works:
- Group components by current version into tiers.
- Upgrade the oldest tier first (highest risk of retirement).
- Run the full test suite after each tier upgrade.
- Update
sourceApiVersioninsfdx-project.jsononly after all components reach the target version.
Why not the alternative: A big-bang upgrade changes runtime behavior across every component simultaneously, making it difficult to isolate regressions.
Pattern 3: CI Pipeline Version Gate
When to use: To prevent version drift from recurring after cleanup.
How it works:
- Add a pre-commit or CI check that scans
*-meta.xmlfor<apiVersion>. - Reject any component whose version is more than 2 major versions behind the project's
sourceApiVersion. - Reject any component below the absolute minimum (currently 31.0).
- Report drift percentage in CI output.
Why not the alternative: Without a gate, developers create new components at the CLI's default version while old components stay on legacy versions, re-introducing drift within a single sprint.
Decision Guidance
| Situation | Recommended Approach | Reason |
|---|---|---|
| All components within 2 versions of target | Big-bang upgrade to current version | Low risk; behavior changes are minimal across adjacent versions |
| Components span 10+ version range | Incremental tier-based upgrade | Isolates regressions; allows targeted test runs per tier |
| External integrations on old versions | Upgrade transport API version separately, coordinated with external teams | Transport version affects API consumers outside your control |
| New project setup | Pin sourceApiVersion to current release, add CI gate immediately |
Prevents drift from day one |
LWC bundles missing explicit apiVersion |
Add <apiVersion> to every .js-meta.xml |
Required from Spring '25; implicit versioning is deprecated |
| Managed package development | Pin to the lowest version your subscribers need | Managed packages must support the subscriber's minimum API version |
Recommended Workflow
Step-by-step instructions for auditing and upgrading API versions across a Salesforce project:
- Read
sfdx-project.json— extract thesourceApiVersionvalue. This is the project's intended baseline. If it is missing, the CLI defaults to the latest version, which may not match component versions. - Inventory all versioned components — scan the metadata source directory for every
*-meta.xml,.cmp,.app, and.js-meta.xmlfile. Extract the<apiVersion>element from each. Record the component name, type, and version. - Identify drift and retirement risk — compare each component's version against
sourceApiVersion. Flag components more than 2 versions behind as drifted. Flag anything below version 31.0 as retirement-critical. - Check runtime API usage — if Event Monitoring is available, query
ApiTotalUsagelogs to find external integrations using deprecated versions. Merge this data with the metadata inventory. - Build the upgrade plan — prioritize retirement-critical components first, then drifted components, then cosmetic alignment. Group into tiers for incremental rollout. Document expected behavior changes per version jump using Salesforce release notes.
- Execute and validate — upgrade each tier by updating
<apiVersion>in metadata files. Run the full test suite after each tier. Confirm deployment succeeds in a sandbox before production. - Add a CI gate — implement a version-check step in the CI pipeline to prevent future drift. Set the minimum acceptable version to no more than 2 behind
sourceApiVersion.
Review Checklist
Run through these before marking work in this area complete:
-
sourceApiVersioninsfdx-project.jsonis set to the target version - No component has an
apiVersionbelow the minimum safe version (31.0) - All components are within 2 major versions of
sourceApiVersion - Every LWC
.js-meta.xmlfile has an explicit<apiVersion>element - External integration endpoint URLs have been checked for deprecated versions
- Full test suite passes at the new version(s)
- CI pipeline includes a version-drift gate
Salesforce-Specific Gotchas
Non-obvious platform behaviors that cause real production problems:
sourceApiVersiondoes not override per-component versions at runtime — many developers assume settingsourceApiVersionto 63.0 means all their Apex runs at 63.0. It does not. Each class executes at the version in its own-meta.xml. ThesourceApiVersiononly affects CLI retrieve/deploy operations.- Apex behavior changes silently between versions — certain System methods change behavior across versions (e.g.,
String.valueOf()on null, SOQL relationship name resolution, trigger context variable availability). An Apex class at version 40.0 can produce different results than the same code at version 63.0, with no compile-time warning. - Retired API versions cause hard failures, not graceful fallbacks — when Salesforce retires a version, REST/SOAP calls to that version return
UNSUPPORTED_API_VERSIONerrors immediately. There is no automatic forwarding to the next supported version. Metadata components on retired versions may fail to deploy.
Output Artifacts
| Artifact | Description |
|---|---|
| API Version Inventory | Spreadsheet or table listing every component, its type, file path, and current API version |
| Version Drift Report | Summary of components that diverge from sourceApiVersion, grouped by severity |
| Upgrade Plan | Prioritized, tiered plan for updating components with test checkpoints |
| CI Gate Configuration | Pipeline step or pre-commit hook that enforces version consistency |
Related Skills
unlocked-package-development— package-levelsourceApiVersionmanagement and subscriber version considerationsscratch-org-management— scratch org definition files reference API versions for feature availabilitysf-cli-and-sfdx-essentials— CLI commands that usesourceApiVersionduring deploy and retrievemetadata-api-and-package-xml—package.xmlversion attribute and its relationship to component versions