Skip to content
Back to Blog
Integration

How to Get an OAuth Access Token in Salesforce

Dec 8, 2025
ATAppnigma Team
How to Get an OAuth Access Token in Salesforce

You get a Salesforce OAuth access token by sending a POST request to the /services/oauth2/token endpoint with a grant type, the consumer key of an external client app, and proof that the caller is allowed in. For a SaaS product that connects to its customers' orgs, that proof is an authorization code from the web server flow with PKCE, and the same response gives you a refresh token and the org's instance URL. The access token is the short-lived part. The refresh token and the instance URL are what your backend has to keep, one set per customer org.

For a SaaS company, the token request is a few lines of code. Keeping a valid token for every customer, after each Salesforce release changes a default, is the work that decides whether the integration your enterprise customers asked for keeps running. Appnigma's integration infrastructure handles "app registration and authorization with infra built for the systems your customers use," and lets "each customer authorize their own account from your product, with their connection managed separately." If you are an admin connecting one org to an internal script, the Salesforce REST API quick start gets you a token through Salesforce CLI with no app to configure.

Updated October 2026. Checked against Salesforce Help, the REST API Developer Guide, the ISVforce Guide and the Winter '27 release notes. The earlier version said access tokens last about 12 hours, listed the username-password flow as an option, recommended the full scope and suggested Named Credentials, which handle calls out of Salesforce rather than an outside app's token. This update adds the full request and response for each flow and what to store per customer. It also covers refresh, revocation and the 30-day refresh token limit that Salesforce enforces in production from November 4, 2026.

Which grant type to send for each kind of integration

The grant type you put in the token request depends on who is present when the token is issued and how many orgs you connect to. Every flow except the SAML assertion flow needs an external client app (ECA), the registration that gives your application a consumer key and lets it use Salesforce OAuth. "All OAuth authorization flows, except for the SAML Assertion flow, require you to define an external client app," per OAuth Authorization Flows.

Flowgrant_type in the token requestFitsRefresh tokenSource
Web server flow with PKCEauthorization_codeA user in each customer org connects from your productYes, with the refresh_token scopeWeb Server Flow
Refresh token flowrefresh_tokenGetting a new access token without the userReturns a new one when rotation is onRefresh Token Flow
Client credentialsclient_credentialsOne org, a run-as integration user, no person presentNot supportedClient Credentials Flow
JWT bearerurn:ietf:params:oauth:grant-type:jwt-bearerBackground jobs for a pre-authorized userNever issuedJWT Bearer Flow
Username-passwordpasswordNothing new, it retires February 20, 2027Not applicableSecurity roadmap

A product that many customers connect on their own needs the web server flow. The two server-to-server flows look simpler in a tutorial, but each one needs an admin in every customer org to set up a run-as user or pre-authorize users before your first request works. Our guide to connecting a SaaS product to customer orgs covers the packaged app and the install step that come before any token request.

Getting a token with the web server flow and PKCE

The web server flow is two requests. First you send the user's browser to the authorize endpoint with a PKCE challenge, then your server swaps the returned code for tokens. Salesforce writes "We recommend that you use the web server flow with Proof Key for Code for Exchange (PKCE, pronounced pixy)," per the web server flow page. PKCE ties each authorization code to a random code verifier your server generated, so a stolen code is useless on its own. ECAs require it by default through the isPkceRequired setting in the ECA OAuth settings metadata.

GET https://login.salesforce.com/services/oauth2/authorize
  ?response_type=code
  &client_id=<consumer key>
  &redirect_uri=https://app.example.com/oauth/salesforce/callback
  &scope=api%20refresh_token
  &code_challenge=<BASE64URL(SHA256(code_verifier))>
  &code_challenge_method=S256
  &state=<random value tied to your customer record>

After the user approves, Salesforce redirects to your callback with code and state. Check that state matches what you sent, then exchange the code from your server. Do it right away, because "The authorization code expires after 15 minutes," per the web server flow page.

curl https://login.salesforce.com/services/oauth2/token \
  -d grant_type=authorization_code \
  -d code=<code from the callback> \
  -d client_id=<consumer key> \
  -d client_secret=<consumer secret> \
  -d redirect_uri=https://app.example.com/oauth/salesforce/callback \
  -d code_verifier=<the original code_verifier>

A confidential client sends both the verifier and the secret. Salesforce asks for "both the code_verifier parameter, for code binding, and the client_secret parameter" in the ISVforce security requirements. The redirect_uri must match a callback URL on the ECA exactly. Sandbox users authorize at test.salesforce.com, and a customer can also give you their My Domain login URL, which the OAuth endpoints page lists as a supported host.

The scope you request decides whether a refresh token comes back. api covers REST API and Bulk API 2.0 calls, and refresh_token lets "the app interact with the user's data while the user is offline," per OAuth Tokens and Scopes. Skip full. It "encompasses all other scopes," yet "full doesn't return a refresh token" unless you also ask for refresh_token, and security review asks for "a comprehensive justification" for it, per Salesforce's review guidance for connected apps and ECAs.

What to store from the token response for each customer org

A Salesforce token response from the web server flow carries more than the access token, and most of it has to be saved per customer org. A response looks like this.

{
  "access_token": "00D...!AQ...",
  "refresh_token": "5Aep...",
  "signature": "...",
  "scope": "refresh_token api",
  "instance_url": "https://customer.my.salesforce.com",
  "id": "https://login.salesforce.com/id/00D.../005...",
  "token_type": "Bearer",
  "issued_at": "1761650000000"
}

instance_url is "A URL indicating the instance of the user's org," per the web server flow page. Every API call for that customer goes to that URL, not to login.salesforce.com and not to a host you hardcoded. The id URL contains the org ID and user ID, which makes the org ID the natural key for the customer's connection record.

Store one record per customer org with the instance URL, the refresh token (encrypted), the granted scopes, the authorizing user and the time of the last successful refresh. The access token itself can live in memory or a short-lived cache. Salesforce's review guidance counts "securely storing auth and refresh tokens" among the secrets a partner app must protect, per KA 005318040. The last refresh time looks like housekeeping. It is the field the 30-day rule depends on.

How long does a Salesforce access token last?

A Salesforce access token lasts as long as the session timeout that applies to it, which is two hours of inactivity unless someone changed it. There is no fixed 12-hour lifetime. Salesforce checks the app's timeout first and the user's profile next. The org's session settings come last, "in that order," per Manage Session Policies. "The default is two hours," per the SOAP API login() reference. Because a customer's admin can change any of those three values, your code shouldn't assume a lifetime at all. Treat a 401 from the API as the signal to refresh, since it means "The session ID or OAuth token used has expired or is invalid," per Status Codes and Error Responses.

The refresh token is the long-lived credential, and its default changed this fall. Salesforce now expires refresh tokens left unused for 30 days. The rule "Applies to external client apps and connected apps, including apps that are installed in subscriber orgs," per the Winter '27 release note. Enforcement reached sandboxes on October 21, 2026 and reaches production orgs from November 4, 2026. "The idle TTL works as a sliding window," so any use resets the clock, per Limit the Idle Refresh Token TTL.

Here is what the documented 30-day window means for a product's sync schedule. A customer whose sync runs daily or weekly uses its refresh token well inside the window. A monthly job breaks after any 31-day month, because the next run lands on day 31. A quarterly export, a paused customer and a trial account that connected and went quiet all lose their token. A scheduled refresh for every connection idle for about 25 days keeps them alive and leaves a few days to retry.

Refreshing, checking and revoking a token

A refresh is one POST to the same token endpoint, sent to the customer's stored host.

curl https://customer.my.salesforce.com/services/oauth2/token \
  -d grant_type=refresh_token \
  -d refresh_token=<stored refresh token> \
  -d client_id=<consumer key> \
  -d client_secret=<consumer secret>

With refresh token rotation on, the response carries a new refresh token and the old one dies. Salesforce tells clients to "overwrite the old refresh token in your secure storage with the new one," per KA 005316711. Partner apps in use by more than two customer production orgs had to turn rotation on by May 11, 2026, per Secure Your Connected Apps and External Client Apps. The same deadline covered PKCE, the 30-day idle limit and a refresh token IP allowlist. So for most SaaS products, rotation is already on.

Rotation makes parallel refreshes dangerous. "Avoid sending simultaneous requests that contain the same refresh token," per the refresh token flow page. Reusing a token that was rotated out makes Salesforce invalidate "the current refresh token and any associated access tokens." Allow one refresh per customer org at a time, with a lock keyed on the org ID, and save the new refresh token before anything uses the new access token.

To check a token without calling a data API, post it to /services/oauth2/introspect. To end a connection when a customer disconnects or churns, post it to /services/oauth2/revoke. Both endpoints are listed on the OAuth endpoints page. Revoke the refresh token, not only the access token. "If a refresh token is included, Salesforce revokes it and any associated access tokens," per Revoking OAuth Tokens. Revoking on disconnect is the clean exit. It also gives the customer's security team one less unexplained app session to ask you about.

Server-to-server tokens with client credentials and JWT bearer

The client credentials flow returns a token for a run-as user that the org's admin picks, with no person present. The request goes to the org's My Domain, because "requests to https://login.salesforce.com and https://test.salesforce.com aren't supported," per the client credentials flow page.

curl https://customer.my.salesforce.com/services/oauth2/token \
  -d grant_type=client_credentials \
  -d client_id=<consumer key> \
  -d client_secret=<consumer secret>

The run-as user "must have the API Only permission," per the ECA OAuth policies metadata, and it is a policy each customer's admin sets in their own org. The flow "doesn't support refresh tokens," so you request a new access token each time the old one expires.

The JWT bearer flow signs a short assertion with your private key instead of sending a secret. Salesforce "requires that a JWT is signed using RSA SHA256, which uses an uploaded certificate as the signing secret," per the JWT bearer flow page. The JWT names your consumer key as the issuer, the Salesforce username as the subject and login.salesforce.com or test.salesforce.com as the audience.

curl https://login.salesforce.com/services/oauth2/token \
  -d grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
  -d assertion=<signed JWT>

The flow "does require prior approval of the client app," which in practice means the customer's admin sets the app to "Admin approved users are pre-authorized" and assigns the user, and it "never issues a refresh token." Both server-to-server flows fit an org you own or a few strategic customers with admins who will do the setup. They don't fit a "Connect Salesforce" button that hundreds of customers click on their own.

Is the username-password flow still an option?

No, not for anything you build now. The username-password flow sends a user's password and security token straight to the token endpoint, and Salesforce has been closing it off in stages. Orgs created in Summer '23 or later block it by default, per OAuth Authorization Flows. It "won't be made available for external client apps," per the connected app and ECA comparison. Salesforce's security roadmap lists the username-password, user-agent and hybrid flows for retirement on February 20, 2027.

For AppExchange and AgentExchange apps, the flow is already out. Salesforce's partner FAQ says "Use of inadequate flows is not allowed for AppExchange use cases," per KA 005387798. If a script of yours still sends grant_type=password, move it to client credentials for an org you own, or to the web server flow when a user should approve access.

Testing a token request in Postman

Postman is useful for seeing the token response before you write code. Create a POST request to https://<host>/services/oauth2/token, set the body type to x-www-form-urlencoded and add the same fields as the curl examples. For the web server flow, Postman's Authorization tab can run the whole exchange with "Authorization Code (With PKCE)" selected, which is how Salesforce's own Postman setup guide for hosted MCP servers configures it. Add Postman's callback URL to your ECA first, or the authorize step fails with a redirect mismatch. For ready-made requests, Salesforce's Connect REST API Postman quick start says to "fork the Salesforce APIs" collection that Salesforce maintains on GitHub.

Then prove the token works with a call that needs one. The versions list at /services/data/ is a poor test, because "You don't need authentication to retrieve the list of versions," per List Available REST API Versions. Call GET <instance_url>/services/data/latest/limits with the header Authorization: Bearer <access_token> instead, using the two values from the token response. The latest alias resolves "to the most recently released API version," per the same page, and List Org Limits returns your org's API allocations, which you will want to watch anyway.

Errors you hit when requesting a token

Most token request failures come back as a short error code in the JSON body, and each one points at a different setting.

ErrorWhat it usually meansWhere to fix it
redirect_uri_mismatchThe redirect_uri doesn't match a callback URL on the ECAECA OAuth settings
invalid_client_idWrong consumer key, or the app isn't in that org yetYour config, or the customer installs your package
invalid_clientWrong consumer secretYour secret store
invalid_grantExpired or reused code, a refresh token rotated out or idle past 30 days, a code_verifier mismatch, or client credentials sent to login.salesforce.comDepends on the flow, see below
invalid_app_accessThe user isn't approved by an admin to use the appCustomer's ECA policies
OAUTH_APPROVAL_ERROR_GENERICThe app isn't installed and the user can't approve uninstalled appsCustomer installs the package

The descriptions come from Salesforce's OAuth 2.0 authorization errors table and the connected app usage restrictions notice. invalid_grant is the one that needs a decision in code. During the web server flow it usually means a retry with the same code or a lost code_verifier. During a refresh it means the token is gone for good, so mark the connection as needing a new login and tell the customer, instead of retrying. Our Salesforce OAuth errors guide goes through each cause in detail.

When to build token handling yourself and when to get help

Writing your own token handling makes sense when Salesforce is the only CRM you integrate with and one engineer can own the ECA, the token store and the refresh job for years. The token requests themselves are the easy part. The ownership is what costs, because Salesforce has changed uninstalled app access, connected app creation and refresh token lifetimes in the past 14 months, and each change landed on integrations that had worked for years.

Bring in help when Salesforce is one of several integrations, or when the engineer who wrote the OAuth code has moved on. Appnigma's integration infrastructure gives your team per-customer connections and monitoring on infra built for the systems your customers use. If you'd rather hand over the build entirely, Appnigma's done-for-you integrations team builds and maintains it for you. For a test ECA to try these requests against, our free external client app tool sets one up.

Frequently Asked Questions

How do I get an access token in Salesforce?

Send a POST to /services/oauth2/token with a grant type and your ECA's consumer key. For customer orgs, use the web server flow with PKCE and request the refresh_token scope.

How long is a Salesforce access token valid?

As long as the session timeout that applies, two hours of inactivity by default. A customer's admin can change it, so refresh on a 401 instead of on a timer.

Can a Salesforce access token be refreshed?

Yes, with the refresh token flow, if you requested the refresh_token scope. Client credentials and JWT bearer issue no refresh token, so you request a new access token instead.

Do I need an external client app to get a token?

Yes for new work. Every flow except SAML assertion needs an ECA or an existing connected app, and new connected apps can't be created since Spring '26.

Can Appnigma handle Salesforce tokens for our customers?

Yes. Appnigma's integration infrastructure handles app registration and authorization and keeps each customer's connection separate.

About the author

Sunny Chauhan is the co-founder and CEO of Appnigma AI, which builds and maintains customer integrations and native Salesforce and HubSpot apps for B2B SaaS teams. Before Appnigma, he was on Salesforce's AppExchange team, where he worked with more than 300 SaaS partners. He has spent close to a decade on the Salesforce platform, including work at Zennify, and holds Salesforce certifications including Platform Developer II, Platform App Builder, Administrator, Data Cloud Consultant, and AI Associate.

Sources

  1. Salesforce Help, OAuth Authorization Flows
  2. Salesforce Help, OAuth 2.0 Web Server Flow for Web App Integration
  3. Salesforce Help, OAuth 2.0 Refresh Token Flow
  4. Salesforce Help, OAuth 2.0 Client Credentials Flow
  5. Salesforce Help, OAuth 2.0 JWT Bearer Flow
  6. Salesforce Help, Salesforce Security Roadmap (KA 005461287)
  7. Salesforce Developers, Metadata API Developer Guide, ExtlClntAppGlobalOauthSettings
  8. Salesforce Developers, ISVforce Guide, Secure Your Connected Apps and External Client Apps
  9. Salesforce Help, OAuth Endpoints
  10. Salesforce Help, OAuth Tokens and Scopes
  11. Salesforce Help, Connected Apps and External Client Apps Security Review Submission Guidance (KA 005318040)
  12. Salesforce Help, Manage Session Policies for a Connected App
  13. Salesforce Developers, SOAP API Developer Guide, login()
  14. Salesforce Help, Winter '27 Release Notes, Refresh Token Idle TTL Is Limited to 30 Days
  15. Salesforce Help, Limit the Idle Refresh Token TTL in External Client Apps
  16. Salesforce Help, Refresh Token Rotation (KA 005316711)
  17. Salesforce Developers, Metadata API Developer Guide, ExtlClntAppOauthConfigurablePolicies
  18. Salesforce Help, Comparison of Connected Apps and External Client Apps Features
  19. Salesforce Help, Partner Security Requirements FAQ (KA 005387798)
  20. Salesforce Developers, Hosted MCP Servers, Configure Postman
  21. Salesforce Developers, Connect REST API, Send a Request with Postman
  22. Salesforce Developers, Mobile SDK, Revoking OAuth Tokens
  23. Salesforce Developers, REST API Developer Guide, List Available REST API Versions
  24. Salesforce Developers, REST API Developer Guide, List Org Limits
  25. Salesforce Help, OAuth 2.0 Authorization Errors
  26. Salesforce Help, Connected App Usage Restrictions (KA 005132365)
  27. Salesforce Developers, REST API Developer Guide, Step Two: Set Up Authentication
  28. Salesforce Developers, REST API Developer Guide, Status Codes and Error Responses

All Blogs

Bring the customer requirements and your current setup. Get a clear scope and delivery timeline to take back to them.