Skip to content

flxbl-io/external-client-apps-guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

External Client Apps (ECA) — Deployment Guide

Background

When setting up external applications to access Salesforce data, we have traditionally created Connected Apps manually per environment. With the move to External Client Apps (ECAs), the goal is to manage and deploy these integrations from source without manual setup per environment.

ECAs split what Connected Apps combine into separate metadata components — separating the app definition, OAuth configuration, secrets, and access policies. This separation enables better packaging, security, and governance but requires a different deployment strategy.

This document outlines the proven deployment patterns, key constraints discovered through testing, and the recommended approach for client credentials flow integrations where the run-as user varies per environment.


ECA Metadata Components

Metadata Type Description Suffix Packageable Source Deployable Managed By
ExternalClientApplication App identity/header .eca-meta.xml Yes Yes Developer
ExtlClntAppOauthSettings OAuth scopes, oauthLink .ecaOauth-meta.xml Yes Yes (but requires valid oauthLink) Developer
ExtlClntAppGlobalOauthSettings Consumer key/secret, flow toggles .ecaGlblOauth-meta.xml No No via source deploy; Yes via Metadata API SOAP (anonymous Apex) Platform
ExtlClntAppConfigurablePolicies Plugin enable/disable .ecaPlcy-meta.xml No Yes (after OAuth is enabled) Org Admin
ExtlClntAppOauthConfigurablePolicies IP relaxation, refresh token, client credentials user .ecaOauthPlcy-meta.xml No Yes (after OAuth is enabled) Org Admin

What Can and Cannot Be Packaged

  • ExternalClientApplication + ExtlClntAppOauthSettings can go in an unlocked package
  • ExtlClntAppGlobalOauthSettings (consumer key/secret) is never in source control — auto-generated by the platform when OAuth is enabled
  • Policies are per-org configuration — deployed via source package, or auto-generated with defaults on package install

The oauthLink Field

The oauthLink in ExtlClntAppOauthSettings references an OAuth Consumer record on the DevHub in the format OrgId:ConsumerRecordId. It is auto-generated when OAuth is enabled via the Setup UI.

Critical constraint discovered through testing:

Never deploy ExtlClntAppOauthSettings without a valid oauthLink. Doing so breaks the OAuth plugin on the target org with the error: "Cannot invoke String.split(String) because oauthLink is null". The oauthLink must be retrieved from the platform — it cannot be manually constructed.


Deployment Patterns

Pattern 1 — Shared Credentials via Unlocked Package (Recommended)

All consumer orgs share the same OAuth consumer key/secret from the DevHub. The run-as user for client credentials flow is configured per environment via a source package with aliasfy.

This pattern minimises manual setup: install the package, deploy environment-specific policies, done.

Repository Structure

src/integrations/
├── eca-definition/                              ← UNLOCKED PACKAGE
│   ├── externalClientApps/
│   │   └── MyApp.eca-meta.xml                   (distributionState: Packaged)
│   └── extlClntAppOauthSettings/
│       └── MyApp_oauth.ecaOauth-meta.xml        (WITH oauthLink from DevHub)
│
└── eca-policies/                                ← SOURCE PACKAGE (aliasfy: true) — OPTIONAL
    ├── prod/
    │   └── extlClntAppOauthPolicies/
    │       └── MyApp_oauthPlcy.ecaOauthPlcy-meta.xml  (client creds ON, prod run-as user)
    ├── uat/
    │   └── extlClntAppOauthPolicies/
    │       └── MyApp_oauthPlcy.ecaOauthPlcy-meta.xml  (client creds ON, uat run-as user)
    └── default/
        └── extlClntAppOauthPolicies/
            └── MyApp_oauthPlcy.ecaOauthPlcy-meta.xml  (client creds OFF — scratch orgs)

The policies source package is optional. When the unlocked package is installed, the platform auto-generates default policies (client credentials disabled, all users self-authorized, standard session level). You only need the policies source package if you require per-environment customization such as:

  • Enabling client credentials flow with a different run-as user per org
  • Changing IP relaxation or refresh token settings per environment
  • Restricting permitted users per org

If the auto-generated defaults are sufficient (e.g., web server flow only), just install the unlocked package — no policies deployment needed.

Initial Setup (One-Time)

  1. Create the External Client App on the DevHub via the Setup UI:

    • Navigate to Setup > External Client Apps > New
    • Set the app name, contact email, and description
    • Enable OAuth plugin
    • Set callback URL, scopes (Api, RefreshToken, etc.)
    • Enable Client Credentials flow if needed
    • Save
  2. Retrieve the ECA and OAuth settings — this captures the platform-generated oauthLink and orgScopedExternalApp:

    sf project retrieve start --metadata "ExternalClientApplication:MyApp" \
      --target-org <devhub>
    sf project retrieve start --metadata "ExtlClntAppOauthSettings:MyApp_oauth" \
      --target-org <devhub>
  3. Copy the retrieved files into the unlocked package source directory:

    • ExternalClientApplicationsrc/integrations/eca-definition/externalClientApps/
    • ExtlClntAppOauthSettingssrc/integrations/eca-definition/extlClntAppOauthSettings/
  4. Set distributionState to Packaged in the retrieved ECA header (it will be Local by default after retrieval)

  5. Commit both files to source control — the oauthLink and orgScopedExternalApp are included as-is

  6. Delete the source-deployed ECA from the DevHub (the package will recreate it on install):

    sf project delete source --metadata "ExternalClientApplication:MyApp" \
      --target-org <devhub> --no-prompt

Ongoing Deployment

# Build all packages (unlocked ECA package + source policies package)
sfp build -v <devhub> --branch <branch>

# Install all packages to the target org
# sfp handles the correct order: unlocked packages first, then source packages
# For source packages with aliasfy, sfp automatically deploys the folder matching the org alias
sfp install -u <target-org>

Client Credentials Flow Configuration

Client credentials flow configuration spans two metadata types:

Setting Metadata Type How to Configure
Enable Client Credentials Flow (app-level toggle) ExtlClntAppGlobalOauthSettings Pattern 1: one-time via Setup UI on DevHub (carries to all consumer orgs via package). Pattern 2: automated via Apex bootstrap.
Execution user (run-as user) ExtlClntAppOauthConfigurablePolicies Source package with aliasfy — clientCredentialsFlowUser field per environment
IP relaxation, refresh token, session level ExtlClntAppOauthConfigurablePolicies Source package with aliasfy
Permitted users policy ExtlClntAppOauthConfigurablePolicies Source package with aliasfy

For Pattern 1, the client credentials flow toggle is set once on the DevHub via the Setup UI. All consumer orgs inherit this through the package — no per-org manual step.

For Pattern 2, the Apex bootstrap script creates ExtlClntAppGlobalOauthSettings with the flow toggle — fully automated, no UI step on any org.

Handling Policies Per Environment

Use aliasfy to vary the OAuth policies per environment. The execution user for client credentials is controlled via the permittedUsersPolicyType and permission set assignments on each org.

prod/extlClntAppOauthPolicies/MyApp_oauthPlcy.ecaOauthPlcy-meta.xml:

<?xml version="1.0" encoding="UTF-8"?>
<ExtlClntAppOauthConfigurablePolicies xmlns="http://soap.sforce.com/2006/04/metadata">
    <clientCredentialsFlowUser>integration-user@prod.example.com</clientCredentialsFlowUser>
    <externalClientApplication>MyApp</externalClientApplication>
    <ipRelaxationPolicyType>Enforce</ipRelaxationPolicyType>
    <isClientCredentialsFlowEnabled>true</isClientCredentialsFlowEnabled>
    <isTokenExchangeFlowEnabled>false</isTokenExchangeFlowEnabled>
    <label>MyApp_oauthPlcy</label>
    <permittedUsersPolicyType>AllSelfAuthorized</permittedUsersPolicyType>
    <refreshTokenPolicyType>SpecificLifetime</refreshTokenPolicyType>
    <refreshTokenValidityPeriod>365</refreshTokenValidityPeriod>
    <refreshTokenValidityUnit>Days</refreshTokenValidityUnit>
    <requiredSessionLevel>STANDARD</requiredSessionLevel>
</ExtlClntAppOauthConfigurablePolicies>

Note: When isClientCredentialsFlowEnabled is true, the clientCredentialsFlowUser field is required. This is the run-as user that varies per environment.

default/ (scratch orgs — client credentials disabled, no user needed):

<?xml version="1.0" encoding="UTF-8"?>
<ExtlClntAppOauthConfigurablePolicies xmlns="http://soap.sforce.com/2006/04/metadata">
    <externalClientApplication>MyApp</externalClientApplication>
    <ipRelaxationPolicyType>Enforce</ipRelaxationPolicyType>
    <isClientCredentialsFlowEnabled>false</isClientCredentialsFlowEnabled>
    <isTokenExchangeFlowEnabled>false</isTokenExchangeFlowEnabled>
    <label>MyApp_oauthPlcy</label>
    <permittedUsersPolicyType>AllSelfAuthorized</permittedUsersPolicyType>
    <refreshTokenPolicyType>SpecificLifetime</refreshTokenPolicyType>
    <refreshTokenValidityPeriod>365</refreshTokenValidityPeriod>
    <refreshTokenValidityUnit>Days</refreshTokenValidityUnit>
    <requiredSessionLevel>STANDARD</requiredSessionLevel>
</ExtlClntAppOauthConfigurablePolicies>

What This Achieves

Concern How It's Handled
App identity + OAuth config Unlocked package (versioned, repeatable)
Consumer key/secret Auto-managed by platform, shared from DevHub
Client credentials flow toggle One-time on DevHub via Setup UI — carries to all consumer orgs via package
Execution user per environment Aliasfy source package — clientCredentialsFlowUser field per org
IP relaxation, refresh token, etc. Aliasfy source package (different policy per org)
Credential rotation Single change on DevHub, no redeployment needed

Pattern 2 — Independent Credentials via Source Package

Each org has its own OAuth consumer key/secret. OAuth is bootstrapped automatically via an anonymous Apex post-deployment script — no manual Setup UI clicks required.

Repository Structure

src/integrations/
├── eca-definition/                              ← SOURCE PACKAGE
│   └── externalClientApps/
│       └── MyApp.eca-meta.xml                   (distributionState: Local)
│
├── eca-policies/                                ← SOURCE PACKAGE (aliasfy) — OPTIONAL
│   ├── <org-alias>/
│   │   └── extlClntAppOauthPolicies/
│   │       └── MyApp_oauthPlcy.ecaOauthPlcy-meta.xml  (client creds ON, org-specific user)
│   └── default/
│       └── extlClntAppOauthPolicies/
│           └── MyApp_oauthPlcy.ecaOauthPlcy-meta.xml  (client creds OFF — scratch orgs)
│
└── scripts/
    ├── bootstrap-eca-oauth.sh                   ← postDeploymentScript (shell wrapper)
    └── bootstrap-eca-oauth.apex                 ← anonymous Apex (creates OAuth consumer)

How It Works

The ExternalClientApplication header is deployed as a source package. A postDeploymentScript runs anonymous Apex that calls the Metadata API SOAP endpoint (upsertMetadata) directly on the target org to create:

  1. RemoteSiteSetting — required for the SOAP callout to the org's own Metadata API
  2. ExtlClntAppOauthSettings — OAuth scopes and settings
  3. ExtlClntAppGlobalOauthSettings — the OAuth consumer (callback URL, security settings)
  4. ExtlClntAppConfigurablePolicies — enables the OAuth plugin

This creates a fully functional OAuth consumer with its own unique key/secret on each org — no manual UI step needed.

sfdx-project.json:

{
  "path": "src/integrations/eca-definition",
  "package": "eca-definition",
  "versionNumber": "1.0.0.NEXT",
  "type": "source",
  "postDeploymentScript": "scripts/bootstrap-eca-oauth.sh"
},
{
  "path": "src/integrations/eca-policies",
  "package": "eca-policies",
  "versionNumber": "1.0.0.NEXT",
  "type": "source",
  "aliasfy": true
}

The bootstrap shell script (bootstrap-eca-oauth.sh):

Based on the approach from lightweight-soap-util, the script inlines anonymous Apex via heredoc and runs it with sf apex run. The Apex makes raw SOAP callouts to the Metadata API upsertMetadata endpoint — no managed package dependency required. It dynamically resolves the org's domain URL (URL.getOrgDomainUrl()) and creates all OAuth components in a single execution. Configuration (app name, scopes) is set at the top of the shell script and injected via sed placeholder replacement.

Setup Flow

  1. Commit the ExternalClientApplication header and the bootstrap scripts to source control
  2. sfp build builds the source package
  3. sfp install deploys the header and runs the post-deployment Apex script
  4. Each org gets its own OAuth consumer with unique credentials
  5. Aliasfy policies deploy per environment (client credentials user, IP relaxation, etc.)
  6. Subsequent deploys of the header do not overwrite OAuth settings already on the org

When to Use This Pattern

  • Strict credential isolation required between environments (regulated industries)
  • Each org connects to a different instance of the external system
  • Organisation policy prohibits shared credentials across orgs
  • Sandbox integrations with non-production systems that cannot protect production credentials

Pattern Comparison

Aspect Pattern 1 (Shared) Pattern 2 (Independent)
Package type Unlocked (org-dependent) Source
Credentials Shared from DevHub Unique per org (auto-generated)
OAuth setup Once on DevHub via UI, retrieve + package Automated via anonymous Apex post-deployment script
oauthLink Required (retrieved from DevHub) Not used (Apex creates OAuth consumer directly)
distributionState Packaged Local
Client credentials user Per-env via aliasfy policies Per-env via aliasfy policies
Credential rotation Single point (DevHub) Per org
Manual effort per org Zero (install package + deploy policies) Zero (deploy header + Apex bootstrap + deploy policies)
Sandbox refresh ECA survives (verified) ECA lost — must redeploy + re-bootstrap
Scratch org pools Works via sfp prepare (package as dependency) Works via sfp prepare (source deploy + Apex bootstrap)
Single point of failure Yes (DevHub OAuth consumer) No

Constraints & Gotchas

  1. Never deploy ExtlClntAppOauthSettings without a valid oauthLink — this breaks the OAuth plugin and cannot be recovered without deleting the metadata

  2. Always retrieve OAuth settings from the platform — the oauthLink is a reference to an auto-generated OAuth Consumer record and cannot be manually constructed

  3. ExtlClntAppGlobalOauthSettings (consumer key/secret) never goes in source control — it is auto-generated when OAuth is enabled via the UI and is managed entirely by the platform

  4. Install the package on the DevHub first — consumer orgs reference the DevHub's OAuth consumer via oauthLink, so the package must exist on the DevHub before installing elsewhere

  5. Policies are auto-generated on package install — you only need to deploy custom policies if the defaults need to change (e.g., enabling client credentials flow, changing IP relaxation)

  6. Redeploying the ECA header does not overwrite OAuth settings — safe for repeated deployments in both patterns

  7. Non-org-dependent unlocked packages require the scratch org definition to point to the correct DevHub — if the project-scratch-def.json references a different org shape, package version creation fails with SH-0001 errors

  8. ExtlClntAppGlobalOauthSettings cannot be deployed to scratch orgs via sf project deploy — the platform returns: "Ephemeral orgs such as scratch orgs can't be used to deploy packaged ECAs." However, it can be created via the Metadata API SOAP endpoint (upsertMetadata) using anonymous Apex running on the org itself — this is how the Pattern 2 bootstrap script works.

  9. ExtlClntAppOauthConfigurablePolicies with client credentials flow fails on scratch orgs if the execution user doesn't exist — use a scratch-org-friendly default policy that does not enable client credentials flow.


Why You Cannot Deploy All ECA Files Together

A common first approach is to retrieve all ECA metadata from one org and deploy it to another. This fails because:

Metadata Error When Deploying to Another Org
ExtlClntAppGlobalOauthSettings "Ephemeral orgs such as scratch orgs can't be used to deploy packaged ECAs" (scratch orgs) or consumer key conflict (sandboxes)
ExtlClntAppOauthSettings "There was a problem with the OAuth link. The app either isn't available on this instance" — the oauthLink references an OAuth Consumer record ID that only exists on the source org
ExtlClntAppOauthConfigurablePolicies "Enter a valid execution user for the OAuth client credentials flow" — the run-as user doesn't exist on the target org

The oauthLink is the core problem: it contains an org-specific OAuth Consumer record reference (OrgId:ConsumerRecordId). This ID is unique to the org where OAuth was enabled and cannot be transferred to another org via source deploy.

The solution: Use an unlocked package. When packaged, the platform handles the oauthLink resolution across orgs. Consumer orgs reference the DevHub's OAuth consumer through the package mechanism.


Development Workflow: Scratch Orgs and Sandboxes

Both patterns support development workflows with scratch org pools (sfp prepare) and sandbox pools. The key consideration is whether lower environments share production credentials or use isolated test credentials.

What Works on Scratch Orgs

Action Works?
Install unlocked package (ECA + OAuth settings with oauthLink) Yes
Source deploy ExternalClientApplication header only Yes
Deploy ExtlClntAppGlobalOauthSettings No — "Ephemeral orgs can't be used to deploy packaged ECAs"
Deploy ExtlClntAppOauthSettings from another org No — oauthLink is org-specific
Deploy ExtlClntAppOauthConfigurablePolicies with client credentials user No — execution user doesn't exist
Deploy ExtlClntAppOauthConfigurablePolicies without client credentials Yes
Deploy ExtlClntAppConfigurablePolicies Yes
Enable OAuth via Setup UI (after deploying header only) Yes

Pattern 1 — Packaged ECA Development Workflow

Create two ECAs on the DevHub — one for production and one for non-production environments. Each is a separate unlocked package with its own OAuth consumer, callback URL, and credentials. This ensures complete isolation between production and development.

When to use: The external system has separate test and production instances, you need credential isolation between development and production, or organizational policy requires that non-production environments cannot access production external systems.

Note: Users can create both ECAs on the same DevHub. The DevHub serves as the central management point for all ECA packages. The "non-production" app is not a lesser version — it is a fully functional ECA with its own OAuth consumer, configured with callback URLs pointing to the test/sandbox instance of the external system.

Repository structure:

src/integrations/
├── eca-definition/                              ← UNLOCKED PACKAGE (production)
│   ├── externalClientApps/
│   │   └── MyApp.eca-meta.xml                   (oauthLink → prod consumer)
│   └── extlClntAppOauthSettings/
│       └── MyApp_oauth.ecaOauth-meta.xml
│
├── eca-definition-nonprod/                      ← UNLOCKED PACKAGE (non-production)
│   ├── externalClientApps/
│   │   └── MyApp_NonProd.eca-meta.xml           (oauthLink → non-prod consumer)
│   └── extlClntAppOauthSettings/
│       └── MyApp_NonProd_oauth.ecaOauth-meta.xml
│
└── eca-policies/                                ← SOURCE PACKAGE (aliasfy) — OPTIONAL
    ├── prod/
    │   └── ...                                  (client credentials ON, prod user)
    ├── uat/
    │   └── ...                                  (client credentials ON, uat user)
    └── default/
        └── ...                                  (client credentials OFF for scratch)

Setup on DevHub:

  1. Create MyApp ECA on DevHub via Setup UI → enable OAuth with production callback URL and scopes → retrieve → package as eca-definition
  2. Create MyApp_NonProd ECA on DevHub via Setup UI → enable OAuth with non-production callback URL (pointing to test/sandbox instance of external system) → retrieve → package as eca-definition-nonprod

sfdx-project.json — dependencies for lower environments:

{
  "path": "src/my-app",
  "package": "my-app",
  "versionNumber": "1.0.0.NEXT",
  "dependencies": [
    {
      "package": "eca-definition-nonprod",
      "versionNumber": "1.0.0.LATEST"
    }
  ]
}

Release configuration — production uses the production package:

The release config for production environments includes eca-definition (not eca-definition-nonprod). The release config for lower environments includes eca-definition-nonprod.

sfp prepare flow (scratch org pools):

  1. Scratch org is created from pool
  2. eca-definition-nonprod package is installed — points to non-production OAuth consumer with non-production callback URL
  3. Policies source package deploys default/ folder
  4. Developer works against the test instance of the external system — no risk to production

Benefits:

  • Complete credential isolation between production and development
  • Non-production callback URL points to the test/sandbox instance of the external system
  • Scratch org developers cannot accidentally interact with production
  • Both packages are managed on the same DevHub — single place for credential management

Pattern 2 — Source + Apex Bootstrap Development Workflow

The ECA header is deployed as a source package with a postDeploymentScript that runs anonymous Apex to bootstrap OAuth.

sfp prepare flow:

  1. Scratch org is created from pool
  2. Source package deploys ExternalClientApplication header
  3. Post-deployment script runs anonymous Apex — creates OAuth consumer, settings, and policies via Metadata API SOAP
  4. Aliasfy policies deploy default/ folder (client credentials OFF)
  5. Each scratch org gets its own independent OAuth consumer and credentials — fully automated

Sandbox Workflow

Sandboxes are non-ephemeral and support all ECA metadata types.

Pattern 1: Install the appropriate unlocked package (production or test) + deploy policies with the correct environment alias (client credentials user for that sandbox).

Pattern 2: Source deploy ECA header + Apex bootstrap creates OAuth automatically + deploy aliasfy policies.

Sandbox Refresh Behavior

Verified behavior:

  • Local ECAs (distributionState: Local, created via source deploy) on prod → verified: NOT replicated to sandboxes on refresh. Tested by deploying AuthorizedTrader and ORDE_ERP2 as Local ECAs to prod, then refreshing sandbox demo55 — neither appeared.
  • Packaged ECAs (installed via unlocked package) on prod → verified: ARE replicated to sandboxes on refresh. ORDE_Middleware, ORDE_ERP, and erp3 (all installed via package) appeared on demo55 after refresh.
  • Scratch orgs always start clean — no ECAs from the DevHub/prod are inherited (verified with fresh scratch org creation).

For Pattern 1, if the unlocked package is installed on prod, sandbox refreshes will include the ECA automatically. No manual steps needed post-refresh — only deploy the aliasfy policies if per-environment customization is required.

For Pattern 2, every sandbox refresh requires: redeploy ECA header + run bootstrap script + deploy policies. With the anonymous Apex bootstrap script, this is fully automated — no manual UI step needed post-refresh.

Client Credentials Flow on Scratch Orgs

The clientCredentialsFlowUser field in ExtlClntAppOauthConfigurablePolicies requires a valid username on the target org. Scratch org usernames are auto-generated (e.g., test-aowz1ztfg0ei@example.com) and cannot be predicted ahead of time.

sfp's replacement system uses org aliases to select environment-specific values but does not provide a built-in variable for the target org's username.

Recommended approach: Keep isClientCredentialsFlowEnabled: false in the default/ aliasfy folder (used for scratch orgs). Developers do not need client credentials flow on scratch orgs — they are testing application logic, not the integration authentication flow. Client credentials testing is done on sandboxes and higher environments where usernames are known and stable.


Verified Deployment Order

Across all org types (scratch, sandbox, production), the deployment order is:

  1. Deploy ECA — via package install (Pattern 1) or source deploy of header only (Pattern 2)
  2. Enable OAuth — automatic via package install (Pattern 1) or via anonymous Apex bootstrap script (Pattern 2)
  3. Deploy policies — via aliasfy source package. Policies cannot be deployed before OAuth is enabled — the platform returns: "The OAuth settings are missing for this external client app."

This order is enforced by the platform and cannot be bypassed.


Summary: Manual Steps Per Pattern

Environment Pattern 1 (Packaged) Pattern 2 (Source + Apex Bootstrap)
Production One-time: create ECA on DevHub via UI, retrieve oauthLink, package Zero — deploy header + Apex bootstrap creates OAuth
Sandboxes Zero — package install + deploy policies Zero — deploy header + Apex bootstrap + deploy policies
Scratch orgs Zero — package install via sfp prepare + deploy policies Zero — deploy header + Apex bootstrap + deploy policies
Post sandbox refresh Zero — deploy policies only Zero — redeploy header + Apex bootstrap + deploy policies

Recommendation

For the use case of an external application accessing Salesforce data via client credentials flow with a different run-as user per environment:

Both patterns are now fully automatable with zero manual steps on consumer orgs. Choose based on your credential management requirements:

Pattern 1 (Packaged — shared credentials) when:

  • You want centralized credential management on the DevHub
  • All orgs should share the same OAuth consumer (or separate prod/non-prod consumers)
  • Sandbox refreshes should preserve the ECA automatically
  • One-time manual setup is acceptable (create ECA + enable OAuth on DevHub, then retrieve and package)

Pattern 2 (Source + Apex bootstrap — independent credentials) when:

  • Each org must have its own unique OAuth consumer key/secret
  • Strict credential isolation is required (regulated industries)
  • You want fully automated deployment with no manual UI steps on any org including the DevHub
  • Each org connects to a different instance of the external system

Both patterns support:

  • Environment-specific client credentials configuration via aliasfy (run-as user per org)
  • Automated scratch org pool creation via sfp prepare
  • Version-controlled, repeatable deployments via sfp build and sfp install
  • Client credentials disabled on scratch orgs (usernames are auto-generated and unpredictable)

About

External Client Apps (ECA) deployment patterns for Salesforce — packaged and source-based approaches with sfp/flxbl

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors