Skip to main content
Planasonix offers two ways to authenticate to a Salesforce org and one transport option that combines with either. This guide covers all of them, the prerequisites they share, and what to do when Salesforce refuses a connection. Every value in the JWT section was verified against a live Salesforce org. Anything not yet verified is marked [unverified] so you know where to spend your scepticism. No statement currently carries that marker. The one that did described mutual TLS as unbuilt. It was never re-checked when that stopped being true, so it went stale by becoming FALSE rather than by becoming verified — the marker told you to doubt a sentence whose problem was not doubt. A marker is a claim about this document’s confidence, and it ages exactly like the claims it annotates.
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.
  1. 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
    Choose Install for Admins Only (or Install for All Users; the app’s own policies still decide who may use it) and finish the install.
  2. 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.
  3. 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.)
  4. 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:
    1. 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.
    2. 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.
    Anyone not covered at the moment the switch is saved loses access. A user with the Use Any API Client permission can use the app regardless of this setting. To undo, set Permitted Users back to All users may self-authorize. These policy settings are per org: they do not change when Planasonix publishes a new package version.
  5. In Planasonix: New connection > Salesforce > Connect with Salesforce, choose Production or Sandbox, and sign in as that user.
Prefer your own app, or need JWT bearer or mutual TLS? Those are the Advanced options in the Planasonix connection form, and the rest of this guide covers them.

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.
So: if you already have a Connected App, keep using it. If you are setting up for the first time, you will create an External Client App. The two are configured in different places and Salesforce uses different names for the same settings. Every step below that differs is given twice. Do not read across from one path to the other:

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.

⚠️ Mutual TLS cannot use a Salesforce Integration licence

The Enforce SSL/TLS Mutual Authentication permission cannot be held by a Salesforce Integration licence. 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 a Salesforce or Salesforce Platform licence from the start. Changing a user’s licence later means revisiting every permission set and profile assignment attached to them.
For OAuth and JWT Bearer, use 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 Integrations for 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 Integration permission-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 is Salesforce 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:
Two things that catch people out:
  • 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 reads acme--sbx.sandbox.lightning.force.com, your My Domain host is https://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:
That suffixed form is the username Planasonix needs — it is part of the username, not the email address. Copy it from Setup → Users → the Username column. A user created inside the sandbox after a refresh may not carry the suffix, so the column is the only authority.

3. OAuth 2.0 (authorization code)

  1. In Planasonix, create a Salesforce connection and choose OAuth 2.0.
  2. Choose Production or Sandbox.
  3. Click Connect. You are sent to Salesforce to sign in and approve access.
  4. Approve, and you are returned to Planasonix with the connection established.
  5. Click Test Connection to confirm.
If you see an approval screen every time, set the app’s App Authorization (ECA) / Permitted Users (Connected App) to Admin approved users are pre-authorized and assign your integration user’s profile or permission set to the app. Salesforce also caps how many times one user may approve the same app, and re-approving repeatedly will eventually hit it. Read §6 on refresh-token expiry before relying on this method 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)
Connected App (only if you already have one) — Setup → App Manager → your app → Edit, and use the Use digital signatures checkbox in place of the Flow Enablement step below.

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; enter https://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: invalid_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 with scope: api and no refresh token — exactly as documented. Do not remove the scope on the reasoning that it is unused.
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 — leave 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 with invalid_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:
  1. Setup → Permission Sets → New. Label it e.g. Planasonix API Access, License --None--.
  2. Setup → Users → your integration user → Permission Set Assignments → Edit → add it.
Then grant that permission set access to the app. For a Connected App this is a button: the app → Manage → Manage Permission Sets. For an External Client App use the app’s Policies tab → Edit → App Policies → Select Permission Sets, and add the set (seen in a live setup on 2026-10-08; if the section is not visible, save the app’s policies once and edit again). If your org does not show that section, the same grant can be created through the API. In the Developer Console → Debug → Open Execute Anonymous Window:
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 reaches https://<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 the Enforce 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.
In Planasonix: open the connection, go to the Mutual TLS panel, enter your My Domain host and paste the PEM bundle (private key followed by the certificate chain).

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 anyway
Use 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 a reasonCode, 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 reaches https://<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
Everything else Salesforce returns is shown. If an object you expect is missing: check the integration user’s permissions in Salesforce (Setup → Users → the integration user → Profile or Permission Sets → Object Settings), not the Planasonix connection. It needs the 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 by error_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.

8. Quick checklist — JWT Bearer

This checklist covers JWT Bearer only. Mutual TLS has different prerequisites — see §5, starting with the licence.