← library
Self-serve, admin-gated
CRM·agent-generated · deepseek-v4.1-flash·checked 2026-10-28
difficulty
2/5

OAuth works self-serve in minutes via an External Client App in Setup — Connected Apps now need a Salesforce Support ticket to re-enable creation — but there is no global app registration, so every customer org's admin must install and pre-authorize your app before the agent can act for their users.

First working connection
~30–60 minutes
Production / approved
~1–5 days (each customer org's admin installs and pre-authorizes; inferred)
Composio managed OAuth
Available: ship today
Composio lists a managed OAuth app for this toolkit — you can ship on it while your own app is set up or reviewed.
who does itagent 4you 5provider 0
evidencetested 3sourced 23inferred 12
◆ agent run4 pages read19/21 quotes verified verbatim60s$0.016deepseek-v4.1-flash

01Pick your path

External Client App (recommended)
when Any new integration, including an agent acting on behalf of a user in an org you control.
→ Self-serve: consumer key/secret in minutes, no Salesforce review, standard authorization-code flow.
Connected App (legacy)
when You already have one, or you're authorizing a Dev Hub for scratch org/sandbox creation.
→ Creation is disabled by default since Spring '26 — needs a Support ticket to enable an org permission plus an admin toggle, then the same OAuth setup. Existing connected apps keep working.
JWT bearer flow (headless)
when The agent runs unattended as a pre-authorized integration user rather than as an interactive user.
→ No consent screen: upload a digital certificate, set 'Admin approved users are pre-authorized', assign profiles/permission sets. You own certificate rotation (inferred).
Package to customer orgs
when Shipping to orgs you don't administer.
→ Each customer's admin installs your app, sets refresh-token policy, and pre-authorizes users. Parallel per-tenant installs; an AppExchange listing would add a security review (inferred — not covered by these sources).

02Time & effort

Hands-on work
2.5 h – 5.1 h
Waiting on others
1 day – 5 days
You're actually working
4% of elapsed time
Phases
6
start↑ first working connection, then production pathproduction
  1. DEVCreate app in Setup15–30 minutessourced[S4][S1]Setup > App Manager > New External Client App; Connected App only if you must.
  2. DEVEnable OAuth, callback, scopes5–15 minutessourced[S4][S1]Click Enable OAuth, set callback URL, tick api and refresh_token/offline_access (web only for browser apps).
  3. DEVSet token policies5–10 minutessourced[S1][S4]Refresh token expiry 90 days or less, session timeout 15 minutes.
  4. DEVCopy consumer key/secret5–10 minutessourced[S4]Email verification code required — developer must be present.
  5. DEVBuild authorize + token exchange + refresh2–4 hoursinferred[S2]Endpoints confirmed by probe; code volume is small, testing in a sandbox org is the variable.
  6. PRODCustomer admin install + pre-authorizewait 1–5 daysinferredPer customer org; the waiting party is each org's Salesforce admin, not Salesforce itself.
agent can do it you waiting on providerbar widths ∝ √time, so short tasks stay visible

03Steps

0/9 done

Get a working connection

  1. Choose External Client App over Connected Appagentsourced ❝[S1][S2][S4]

    Connected Apps are deprecated and creation is off by default; External Client Apps are the preferred framework and are self-serve. Only pick a Connected App for Dev Hub scratch-org/sandbox flows or an app that already exists.

    “Otherwise, create an external client app, which is the preferred integration framework.” — [S1] verified verbatim
    ↗ developer.salesforce.com/docs/platform/sfdx-dev/guide/sfdx-dev-auth-eca.html
    ◆ prepared by agent · App-type decision note
    Decision: External Client App (not Connected App). Reason: connected-app creation is disabled by default as of Spring '26 and requires a Salesforce Support ticket to enable an org permission; External Client Apps are the supported path and are self-serve.
  2. Create the app record in the target orgyousourced ❝[S4]

    Setup > Quick Find 'App Manager' > App Manager > New External Client App. Fill in the name and your contact email. This must be done by someone logged into the org.

    “From the Quick Find box in Setup, enter App Manager, then click App Manager.” — [S4] verified verbatim
  3. Enable OAuth and pick the narrowest scopesyousourced ❝[S4][S1]

    Under 'API (Enable OAuth Settings)' click Enable OAuth; set the Callback URL to your agent's HTTPS redirect (http://localhost:1717/OauthRedirect while testing); tick api and refresh_token/offline_access, and web only if a browser is involved.

    “Under API (Enable OAuth Settings), click Enable OAuth.” — [S4] verified verbatim
  4. Lock down token lifetime and permitted usersyousourced ❝[S4][S1]

    Policies tab: set 'Expire refresh token after' 90 days (or less) and Session Timeout 15 minutes. For JWT, set Permitted Users to 'Admin approved users are pre-authorized' and assign profiles/permission sets.

    “In the Refresh Token Validity Period box, enter 90. For Refresh Token Validity Unit, select Day(s).” — [S4] verified verbatim
  5. Retrieve Consumer Key and Secretyousourced ❝[S4]

    App Manager > External Client App Manager > your app > Settings > OAuth Settings > Consumer Key and Secret. Enter the emailed verification code, then copy both values into your secret store. The agent cannot do this step.

    “Open OAuth Settings and click Consumer Key and Secret.” — [S4] verified verbatim
  6. Implement authorization-code flow against the probed endpointsagentinferred

    Redirect to /services/oauth2/authorize with client_id, response_type=code, redirect_uri; exchange the code at /services/oauth2/token. A 400 'invalid_client_id' at the authorize step means the app record or host is wrong — the probe confirms that error is the signature of a missing app.

    ◆ prepared by agent · Token exchange + refresh against the probed endpoints
    POST https://login.salesforce.com/services/oauth2/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    &code=<code from callback>
    &client_id=<CONSUMER_KEY>
    &client_secret=<CONSUMER_SECRET>
    &redirect_uri=https%3A%2F%2Fyour-agent.example.com%2Foauth%2Fcallback
    
    # refresh
    POST https://login.salesforce.com/services/oauth2/token
    grant_type=refresh_token&refresh_token=<rt>&client_id=<KEY>&client_secret=<SECRET>
    
    # sandbox: swap host to https://test.salesforce.com

Make it production-ready

  1. Handle refresh and the 90-day reauthorizationagentsourced ❝[S4][S1]

    Refresh before the 15-minute session timeout; when the 90-day refresh token expires, send the user back through /services/oauth2/authorize. Don't build a silent-retry loop that fails forever.

    “To continue running CLI commands against an org whose refresh tokens have expired, reauthorize it with the org login web or org login jwt command.” — [S4] verified verbatim
  2. Add the headless path if users aren't at the keyboardyousourced ❝[S4][S1]

    For an unattended agent: enable JWT bearer flow, upload a certificate, set pre-authorized profiles/permission sets, and use 'you@org' style subject logins. You must rotate the certificate before it expires (expiry handling is inferred).

    “_(Required for JWT)_ In the Flow Enablement section, select Enable JWT Bearer Flow.” — [S4] verified verbatim
  3. Package and hand off to each customer orgagentinferred

    If other orgs will use your agent, each admin installs the app, sets the refresh-token policy, and pre-authorizes users. Ship a one-page admin checklist — the agent can draft it; the admin click-through is theirs.

04Requirements, scopes & nuances

You'll need

  • ▸Salesforce org with Setup access (admin) to create an External Client App or Connected App. inferred[S1][S4]
  • ▸Consumer Key (client_id) and, for the web server flow, Consumer Secret — plus email access to pass the verification-code gate. sourced ❝[S1][S4]
  • ▸A registered callback URL (the CLI guide uses http://localhost:1717/OauthRedirect for local testing; use your own HTTPS redirect in an agent). sourced ❝[S4]
  • ▸For JWT/headless: a digital certificate (server.crt) uploaded to the app and profiles/permission sets marked pre-authorized. sourced ❝[S4][S1]
  • ▸For Connected Apps specifically: a Support-enabled org permission and the admin toggle 'Allow creation of connected apps'. sourced ❝[S1]

Gotchas

  • !The OAuth scopes are chosen on the app record (api / web / refresh_token, offline_access), not requested at authorize time, so scope minimization is a Setup decision you make once per app. inferred
  • !The discovery document's scopes_supported does NOT list api, web, or refresh_token — it lists CDP/Chatter/Wave/Einstein scopes instead — so don't build your scope allow-list from the probe output; use the app's OAuth Scopes checkboxes. inferred
  • !Refresh tokens are capped: both guides recommend 'Expire refresh token after' 90 days or less, so any agent running longer than that needs a reauthorization path, not just a refresh loop. sourced ❝[S1][S4]
  • !Access-token session timeout is set on the app (15 minutes in the Salesforce CLI guide); your agent must refresh proactively rather than assume a long-lived access token. sourced ❝[S1][S4]
  • !Sandbox and production are different issuers and endpoints (test.salesforce.com vs login.salesforce.com); credentials and callbacks must be registered against the right one. inferred
  • !Connected Apps are deprecated and being replaced by External Client Apps; building new on Connected Apps means a Support dependency you can avoid. sourced ❝[S1][S2][S4]
Scope / permissionTierNoteEvidence
apibasic'Manage user data via APIs (api)'. The one you actually need for REST/SOQL work.sourced ❝[S4][S1]
refresh_token / offline_accesssensitive'Perform requests at any time'. Grants long-lived access — treat as sensitive and pair with the 90-day expiry policy.sourced ❝[S4][S1]
webbasic'Manage user data via Web browsers (web)'. Only needed if the agent's flow involves browser-based login; drop it for pure server-side exchange if your flow allows.sourced ❝[S4][S1]
einstein_gpt_api, wave_api, chatbot_api, cdp_* gatedPresent in scopes_supported on both hosts, but nothing in these sources explains their requirements. Assume product licenses and org provisioning are needed before they can be granted (inferred).inferred

05If you get blocked

✕Consumer Key/Secret retrieval triggers an email verification code, so an agent working alone stalls at credential pickup.inferred

→ You complete it once when you copy the Consumer Key/Secret, then store both in your secret manager and feed them to the agent's config.

✕No app can be created without org admin rights — Setup > App Manager is not available to standard users.inferred[S1][S4]

→ You need Setup access (System Administrator or delegated admin) in the target org; if it's a customer's org, their admin creates/installs the app and reads out the consumer key to you.

✕Creating a Connected App fails because creation is disabled by default as of Spring '26.sourced ❝[S2][S1]

→ Use an External Client App instead (fully self-serve), or have an admin open Setup > External Client Apps > Settings and turn on 'Allow creation of connected apps' once Support has enabled the org permission.

✕JWT/headless flows need 'Admin approved users are pre-authorized' plus profile/permission-set assignment, or every token request is rejected.sourced ❝[S1][S4]

→ You assign profiles/permission sets in the app's Policies tab; the agent can draft which permission sets are needed but cannot grant them.

✕There is no global Salesforce app registration — every customer org holds its own app record and must authorize separately, so 'sign in with Salesforce' for arbitrary users is a per-tenant install, not a single OAuth client.inferred

→ Design for per-org configuration (each tenant supplies its own consumer key or installs your package) rather than one global client_id; start with one pilot org.

06Evidence: what was tested, sourced, and still uncertain

$ probes: live GETs with a dummy client_id, no credentials
✓ Discovery document (/.well-known/openid-configuration) → 200
https://login.salesforce.com/.well-known/openid-configuration
issuer=https://login.salesforce.com; authorization_endpoint=https://login.salesforce.com/services/oauth2/authorize; token_endpoint=https://login.salesforce.com/services/oauth2/token; scopes_supported=cdp_ingest_api custom_permissions cdp_segment_api content cdp_api chatbot_api cdp_identityresolution_api interaction_api wave_api web cdp_calculated_insight_api einstein_gpt_api
∴ Live OIDC discovery for production: authorization and token endpoints are /services/oauth2/authorize and /services/oauth2/token. Hardcode these; don't scrape Salesforce Help for them. Note the scopes_supported list does not include api/web/refresh_token, so it is not a usable scope catalogue.
✓ Discovery document (/.well-known/openid-configuration) → 200
https://test.salesforce.com/.well-known/openid-configuration
issuer=https://test.salesforce.com; authorization_endpoint=https://test.salesforce.com/services/oauth2/authorize; token_endpoint=https://test.salesforce.com/services/oauth2/token; scopes_supported=cdp_ingest_api custom_permissions cdp_segment_api content cdp_api chatbot_api cdp_identityresolution_api interaction_api wave_api web cdp_calculated_insight_api einstein_gpt_api
∴ Sandbox equivalent. Same paths, different host and issuer — so your config must be host-parameterized, and tokens from test.salesforce.com are useless in production.
✓ Authorize endpoint with a fake client_id → 400
https://login.salesforce.com/services/oauth2/authorize?client_id=probe_invalid_client&response_type=code&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
— body mentions "error=invalid_client_id&error_description=client%20identifier%20invalid"
∴ The authorize endpoint is live and rejects an unregistered client_id with HTTP 400 'invalid_client_id'. That error, not a login page, is what you'll see while your app record is missing, disabled, or you're hitting the wrong host — a fast pre-flight check for the agent.

Sources read

  1. [S1] Salesforce Developers official
  2. [S2] Salesforce Developers official
  3. [S3] Sitemap official
  4. [S4] Salesforce Developers official
  5. [composio] Composio — Managed OAuth apps directory official
  6. [probe] Live HTTP probes run by the agent (dummy client_id, no credentials) probe

Still uncertain

  • ? No source covers distribution mechanics or AppExchange security-review timelines, so the 'package to customer orgs' path and its wait are inferred.
  • ? The discovery document's scopes_supported omits api/web/refresh_token, which contradicts the Setup-guided scope list; the authoritative scope set is the app record, per the sourced guides.
  • ? No source describes the user-consent screen contents or whether end users see per-scope consent, so what your users actually approve is unknown.
  • ? API rate limits, edition/licence requirements, and My Domain vs login.salesforce.com login hosts are not covered by these sources — treat host choice beyond the two probed issuers as inferred.
  • ? Nothing here documents Salesforce's own review of third-party apps at the OAuth layer; the provider-side gate in practice is each customer org's admin, not Salesforce.
  • ? The two time estimates for building the flow and for customer-side rollout are judgement calls, not sourced figures.
  • ? Generated automatically; no app was registered and no review was submitted.