Quick start: Planasonix Connect (recommended)
Planasonix Connect is the Salesforce app Planasonix publishes, so you do not create an app of your own. Your Salesforce admin installs it once per org; after that, anyone who may connect clicks Connect with Salesforce in Planasonix. It contains no code and no data: only the two OAuth apps (Planasonix Connect, and Planasonix JWT for JWT sign-in) that let Planasonix ask your org for access. Package version: 0.2 (released). The links below install exactly that version.-
A Salesforce admin installs it, once per org. Open the link for your org type while signed in as an administrator:
- Production or Developer Edition:
https://login.salesforce.com/packaging/installPackage.apexp?p0=04tgK000000VqFFQA0 - Sandbox:
https://test.salesforce.com/packaging/installPackage.apexp?p0=04tgK000000VqFFQA0
- Production or Developer Edition:
- Leave the refresh token policy as installed, and plan to reconnect within 365 days. The package installs the app with Refresh Token Policy set to Expire refresh token after specific time, 365 days. Do not change it. The installed app offers only Immediately expire refresh token, Expire refresh token after specific time and Expire refresh token if not used for specific time; there is no valid until revoked option. So plan to reconnect a Connect with Salesforce connection within 365 days of making it: open the connection in Planasonix and click Reconnect. Treat 365 days as the planning figure, not an exact date: Refresh Token Rotation, or an admin changing the policy, can make the real expiry differ. When the refresh token does expire, every scheduled sync on that connection fails until someone reconnects. Your org may also cap idle tokens (§6). For unattended pipelines that must never need re-authorising, use JWT Bearer, which issues no refresh token.
- Choose the Salesforce user Planasonix acts as, and give it the access it needs. Planasonix reads and writes as the user who approves the connection, so that user decides what Planasonix can see. Use a dedicated integration user, not a person: either a user with the Salesforce Integration user license and the Salesforce API Only System Integrations profile (or a clone of it), or a standard user whose profile or a permission set grants API Enabled. Then create a permission set of your own (Setup > Permission Sets) that grants API Enabled and Read on exactly the objects your pipelines use, and assign it to that user. Field-level security and sharing still apply. Do not grant Modify All Data. (Planasonix does not ship this permission set: a package cannot carry permissions on standard objects such as Account or Contact.)
-
Recommended: pre-authorize the integration user, so no one is shown an approval screen. Without this, the first sign-in shows Salesforce’s Allow Access screen, which can carry a security warning about the app. Pre-authorized users skip it; anyone not covered is refused. Two separate grants are involved: the permission set from step 3 gives the user API and object access, and app authorization lets that permission set use this app. Do them in this order, so no one is cut off:
- Authorize the permission set for this app. In Setup > External Client App Manager > Planasonix Connect > Edit Policies, give the permission set from step 3 (or the integration user’s profile) access to the app, and save. If your org shows no control for this, create the grant as in §4.4, which does not depend on the Permitted Users setting.
- Then switch the app to pre-authorization. On the same screen, under OAuth Policies, set Permitted Users to Admin approved users are pre-authorized, and save. If the permission-set control only appears after this switch, make both changes in the same edit and save once.
- In Planasonix: New connection > Salesforce > Connect with Salesforce, choose Production or Sandbox, and sign in as that user.
1. Choose a method
There are two authentication methods and they are both OAuth.urn:ietf:params:oauth:grant-type:jwt-bearer is an OAuth 2.0 grant type, so “OAuth or JWT” names one protocol twice. What differs between them is who authorises, and whether a refresh token exists to expire.
If you are automating scheduled data movement, choose JWT Bearer. It issues no refresh token, so there is nothing that can quietly expire between runs and no re-authorisation to perform. The other method depends on a refresh token that your org’s policies may expire (see §6).
Mutual TLS is not a third method
Mutual TLS is a transport option. You choose it in addition to one of the two methods above, never instead of either. It is not an alternative way to log in, and a connection configured with a certificate and no credentials is not configured. The two layers answer different questions, which is why neither substitutes for the other:
Because they are separate layers, the Salesforce integration user needs permissions from both:
Enforce SSL/TLS Mutual Authentication and API Only. Granting one without the other leaves a connection that fails at whichever layer was left out — see §5, where all four preconditions are listed.
2. Before you start — this applies to all three methods
2.1 Connected Apps vs External Client Apps
Salesforce changed this in Spring ‘26:- Creating a new Connected App is disabled by default in all orgs. Enabling it requires a case with Salesforce Support.
- New integrations use an External Client App (ECA). Existing Connected Apps continue to work and can still be edited.
2.2 Create the integration user — the licence depends on the method
Use a dedicated user, not a person’s login. Which user licence you need is decided by the authentication method, so choose the method first.For OAuth and JWT Bearer, use⚠️ Mutual TLS cannot use a
TheSalesforce IntegrationlicenceEnforce SSL/TLS Mutual Authenticationpermission cannot be held by aSalesforce Integrationlicence. A user on that licence cannot be given the permission, so mutual TLS cannot be enabled for them — and nothing tells you this until you go looking for the permission and find it unavailable. If you need mutual TLS, create the user on aSalesforceorSalesforce Platformlicence from the start. Changing a user’s licence later means revisiting every permission set and profile assignment attached to them.
Salesforce Integration — the licence Salesforce provides for API-only integrations. We have verified the JWT flow end to end on that licence with the Minimum Access – API Only Integrations profile, so it is sufficient. Note that it requires the app authorisation step in §4.4, which an administrator’s own login does not.
Whichever licence you use:
- Profile:
Minimum Access – API Only Integrationsfor OAuth and JWT. For mutual TLS the user must also be API Only (§5), which is a profile setting rather than a licence one. - Grant object and field access through permission sets — Account, Contact, Opportunity, custom objects, and Bulk API operations as your pipelines require. Keep them separate from the profile so they can be changed without re-provisioning the user.
- On a Salesforce Integration licence, assign the
Salesforce API Integrationpermission-set licence (PSL). Without it, CRM objects such as Account and Contact are not available to the integration user (measured on a sandbox, 2026-10-09). Then grant data access through a permission set whose License isSalesforce API Integration, with object Read (and View All Records if every record should be visible) and field Read — fields are not granted by default, and an object without field Read appears with columns missing.
2.3 Find your My Domain host — do not construct it
Several steps need your org’s My Domain host, and Planasonix never builds it for you, because it cannot be derived reliably. Get it from: Setup → Quick Find → My Domain → Current My Domain URL. It looks like:- A sandbox’s name is not always related to your org’s name. It is whatever the sandbox was called when it was created, and it can be a near-miss of your domain that is very hard to spot by eye. Copy it; do not retype it.
- Your browser’s address bar shows
*.lightning.force.com, not*.my.salesforce.com. They share the left-hand part. If the bar readsacme--sbx.sandbox.lightning.force.com, your My Domain host ishttps://acme--sbx.sandbox.my.salesforce.com.
2.4 Sandbox usernames carry the sandbox name
When a sandbox is refreshed, Salesforce appends the sandbox name to every username in it:3. OAuth 2.0 (authorization code)
- In Planasonix, create a Salesforce connection and choose OAuth 2.0.
- Choose Production or Sandbox.
- Click Connect. You are sent to Salesforce to sign in and approve access.
- Approve, and you are returned to Planasonix with the connection established.
- Click Test Connection to confirm.
4. OAuth 2.0 + JWT Bearer — recommended for scheduled pipelines
Planasonix signs a short-lived assertion with a private key that never leaves our credential store, and Salesforce exchanges it for an access token. No password, no refresh token, no browser login after setup.4.1 Get the certificate
Planasonix generates an RSA-2048 key pair for each connection and gives you the public certificate to upload. The private key stays in our credential store and is never transmitted. A self-signed certificate is sufficient for this flow — the certificate verifies our signature, it does not establish a TLS identity. You do not need a CA-issued certificate, a certificate chain, or a certificate purchase. In Planasonix: create the connection, open the Your own app — JWT + certificate tab, fill in Consumer Key and Salesforce Username, then click Generate certificate and download it. Generate refuses until both fields are filled. The Consumer Key does not exist until you create the app (§4.5), so type a placeholder now and paste the real key before you save (§4.6): sign-in reads the key from the saved connection, not from the certificate.4.2 Create the app in Salesforce
External Client App — Setup → Apps → External Client Apps → External Client App Manager → New:
Then, on the same screen:
- ✅ Enable OAuth — tick this
- ⬜ Enable SAML — leave off (browser single sign-on, unrelated)
- ⬜ Canvas App Settings — leave off (embedding a UI inside Salesforce, unrelated)
4.3 OAuth settings
Callback URL — required by Salesforce even though the JWT flow never redirects a browser. The JWT tab shows no callback URL because none is used; enterhttps://login.salesforce.com/services/oauth2/success.
OAuth Scopes — select BOTH of these:
The second one is not optional, and it looks as though it should be. The JWT flow never issues a refresh token — but Salesforce requires that scope to be granted before it will mint a token without an interactive approval. The scope names a capability, not a token. If you leave it out, Salesforce returns:Flow Enablement — tick Enable JWT Bearer Flow, then upload the public certificate Planasonix gave you, using the Certificate Upload control directly beneath that checkbox. The certificate must be 4 KB or smaller. Leave the other flows off unless you need them: Client Credentials, Authorization Code and Credentials, Device, Token Exchange. Security section — leaveinvalid_request— “refresh_token scope is required and the connected app should be installed and preauthorized.” Verified: with both scopes selected, the token response comes back withscope: apiand no refresh token — exactly as documented. Do not remove the scope on the reasoning that it is unused.
Issue JSON Web Token (JWT)-based access tokens for named users OFF.
This is a different setting from the one above and it is easy to mistake for it. It controls whether the access token Salesforce returns to you is itself a JWT. It has nothing to do with whether Salesforce accepts your JWT, and you do not need it.Some settings in this section may be locked by your org’s security baseline and marked “To change this required setting, contact Support.” That is normal and does not affect JWT — but see §6, because two of them affect OAuth.
4.4 Authorise the integration user — the step with no user interface
This step is mandatory and Salesforce provides no screen for it on an External Client App. Skip it and authentication fails withinvalid_app_access — “user is not admin approved to access this app.”
An administrator testing with their own login will not hit this, because administrators bypass the check. Every service account needs it.
Do the three parts in this order. Switching the app to pre-authorization refuses every user it does not already cover, so the grant comes before the switch.
First create a permission set and assign it to the integration user:
- Setup → Permission Sets → New. Label it e.g.
Planasonix API Access, License--None--. - Setup → Users → your integration user → Permission Set Assignments → Edit → add it.
SetupEntityType is derived from the Id and is not set explicitly. A DUPLICATE_VALUE error means the grant already exists, which is a success.
To confirm it landed:
If your administrator will not run anonymous Apex, the same record can be created through the Metadata API, or Salesforce Support can assist. We have raised the missing interface with Salesforce.Last, on the app’s Policies tab → OAuth Policies → Plugin Policies → Permitted Users → Admin approved users are pre-authorized, and save. The grant above does not depend on this setting, so it can be made first.
The same setting is shown as App Authorization on the app’s read-only detail summary and as Permitted Users on the edit form, under a section headed Plugin Policies. One setting, two names, two screens.A note on what does not work, so you do not spend time on it: setting Permitted Users to All users may self-authorize does not help, because self-authorisation requires the user to approve the app once in a browser — and an API-Only user cannot sign in to a browser. The
Use Any API Client permission would bypass the check, but it is not present in every org and it grants far more than this app.
4.5 Get the Consumer Key
External Client App: Manage External Client Apps → your app → Settings → OAuth Settings → Consumer Key and Secret. Connected App: your app → Manage Consumer Details. You need only the Consumer Key. The JWT flow does not use the consumer secret — despite the page offering both.4.6 Finish in Planasonix
Click Test Connection, then Save. On releases where the test does not yet perform the JWT sign-in, a passing test only proves Salesforce is reachable; the proof that JWT works is that the table list loads after saving.
5. Mutual TLS
Mutual TLS authenticates the transport with a client certificate, on top of whichever method authenticates the user. It does not replace OAuth or JWT — it runs underneath one of them. Salesforce serves mutual authentication on port 8443 of your My Domain host, not on the standard port (Salesforce Help, Set Up a Mutual Authentication Certificate for API Login). A connection bound to mutual TLS reacheshttps://<yourdomain>.my.salesforce.com:8443, never https://<yourdomain>.my.salesforce.com. Planasonix
switches the host and port for you when a certificate is bound; you do not configure it.
5.1 The licence rule — settle this before creating anything
The Salesforce user your connection logs in as must hold a licence that permits theEnforce SSL/TLS Mutual Authentication permission.
The Integration licence is the trap, because it is the right answer for the other two
methods and the wrong one here. If you created an integration user for JWT Bearer (§2.2)
and now want mutual TLS on the same user, the licence has to change — it is not a
permission you can grant your way out of. Either move that user to a full
Salesforce
licence, or point the connection at a different user who already holds one.
A licence that permits a permission is not the same as the permission having been granted. You need both. Planasonix checks them separately and names which one failed.
5.2 What Planasonix checks before it will enable mutual TLS
Enabling mutual TLS on the wrong account can lock your org out of its own Salesforce UI, so Planasonix refuses to enable it until it has checked the org and can say the way back out exists. The checks run against your org’s real users at the moment you click Attach. 1 — We must be able to read your org’s users. The connection’s user needs View All Users. Every other check is a statement about the whole user list, and without it there is nothing to check. This is not a judgement that anything is unsafe; it is that we could not look. 2 — The user must hold the mutual-auth permission.Enforce SSL/TLS Mutual Authentication, under System Permissions, on either their profile or a permission set
assigned to them. Either door works, and the permission set is usually the cheaper one.
3 — The user must be API Only. Salesforce requires this. Its guidance on Set Up a Mutual Authentication Certificate for API Login
says to assign the permission to an API-only user account, and states:
This user must have the API Only User permission.Setting API Only ends UI login for that account immediately, whether or not mutual TLS is ever enabled — so set it on a dedicated integration account, never on a person’s login. This is why check 6 exists. 4 — The profile must be dedicated to this user. If the mutual-auth permission is carried by a profile that other users share, enabling it here would impose mutual TLS on all of them. Give the connection’s user a profile of their own. 5 — The permission set must be assigned to this user only. Same reason as the profile, one level down. If you granted the permission through a permission set, that set must not be assigned to anyone else. 6 — An administrator must be able to undo this. An active, non-API-only administrator, other than the connection’s user, who can remove the permission or delete the certificate. Because check 3 has just taken UI login away from the connection’s user, this is the only route back if anything goes wrong. Check 6 is the one that often cannot be answered automatically. Salesforce has no single field that says “this person can turn mutual auth off”, so Planasonix frequently finds it undetermined rather than false — which is not the same as finding that no such administrator exists. When that happens you confirm it yourself; see §5.5.
5.3 Getting the certificate
Mutual TLS is the one flow where Planasonix does not generate the certificate for you. Unlike JWT Bearer (§4.1), where we generate the key pair and hand you a certificate to upload, mutual TLS needs a certificate that Salesforce will trust as a TLS identity:- Use a certificate from a public certificate authority (CA), as Salesforce’s guidance describes (Set Up a Mutual Authentication Certificate for API Login).
- It must be presented with its intermediate chain, leaf first.
- You supply the private key and the chain to Planasonix; the key goes straight into our credential store and is never returned by any endpoint.
5.4 Uploading the certificate in Salesforce
Setup → Certificate and Key Management → Upload Mutual Authentication Certificate. Deleting a mutual-auth certificate is not instant. Salesforce’s guidance notes that removing one can take up to 5 minutes to take effect. If you delete a certificate and connections keep succeeding, wait before concluding the deletion failed.Salesforce has three separate certificate uploads and they are not interchangeable: the mutual-auth certificate above · the JWT signing certificate from §4.3 · self-signed and CA-signed certificates for outbound calls. Uploading one in place of another fails in ways that do not name the mistake.
5.5 When the recovery-admin check cannot be answered
If every other check passes and only check 6 is undetermined, Planasonix shows a single button:An admin can undo this — attach anywayUse it once you have confirmed in Salesforce that an active, non-API-only administrator other than the connection’s user can remove the permission or delete the certificate. The button attaches immediately and records your reason in the audit log. The reason arrives filled in and you can edit it before clicking; clearing it disables the button, because an override with no stated reason is not an override we will record. This confirmation covers check 6 and nothing else. It is not available for any other refusal, and it does not skip checks 1–5.
5.6 Troubleshooting — keyed on the code in the response
Planasonix returns a machine-readable code with every refusal. Two surfaces report them slightly differently: the connection panel returns areasonCode, and where the checks
actually ran it adds a preconditionCode naming the specific condition. The Settings
preconditions checker returns that same specific code directly.
reasonCode — what kind of answer you got
preconditionCode — which condition failed
5.7 Sandboxes
The My Domain host carries the sandbox name, and so does the username (§2.4). A sandbox connection reacheshttps://<yourdomain>--<sandboxname>.sandbox.my.salesforce.com:8443.
A sandbox is a separate Salesforce org. Upload the certificate in the sandbox as well,
and satisfy the checks in §5.2 there too — the licence, the permission, API Only, the
dedicated profile, the permission set and the recovery administrator are all properties of
that org’s users, not of production’s. A refresh recreates the sandbox from production, so
expect to redo this after one.
The form asks the server whether this deployment offers the method and withholds it if not, so an absent tab means the server reported it unavailable.
5a. The objects you see are the objects your integration user can query
If the table list looks short, this is almost always why, and it is not a fault. Planasonix asks Salesforce which objects exist, and Salesforce answers for the authenticated user. Two connections to the same org, authenticating as different users, will list different objects — and a sandbox that is a copy of production can list far fewer than production does, because the copy’s user is not the production user. Measured against a sandbox org: the same product returned 715 objects for one connection and 204 for another. Nothing was missing from the org and nothing was dropped in transit. The short list belonged to a user without read access to most objects. What Planasonix does with Salesforce’s answer:- objects Salesforce marks not queryable are omitted — they cannot be read by any query, so listing them would offer you a table that always fails
- custom settings are omitted — they are configuration rather than data
Salesforce API Integration permission-set licence on an Integration licence, object Read, and field Read on the fields you want (§2.2). Then refresh the table list.
A related surprise: a few objects refuse a plain unfiltered query and require a filter, so they can appear in the list and still fail when previewed. Salesforce names the object in the error when this happens. We have not mapped which objects are affected, so we are not publishing a list rather than publishing a wrong one — if you hit it, the error text is the reliable signal and is the thing to send us.
6. Token expiry — read this if you run scheduled pipelines
This section does not apply to JWT Bearer, which issues no refresh token and therefore has nothing that can expire. For OAuth 2.0, your connection depends on a refresh token, and three Salesforce settings govern how long it lives. In newer orgs these may be enforced at the org level and marked “To change this required setting, contact Support”:
What this means in practice: an OAuth connection must be used within the idle window, and a Planasonix Connect connection should be reconnected within 365 days of connecting (the installed policy), or it will need re-authorising. If your pipelines run less often than the idle window, or must never need a person to reconnect, use JWT Bearer instead.
To re-authorise: open the connection in Planasonix and click Reconnect.
7. When Salesforce refuses — keyed on the exact message
Salesforce distinguishes most failures only byerror_description, so match on that text rather than the error code.
If you see a message that is not in this table, send us the
error and error_description exactly as Salesforce wrote them. Those two lines are the diagnosis; a plausible-sounding guess based on anything else will usually point at the wrong setting.