System.CalloutException: SSLHandshakeException: PKIX path building failed: unable to find valid certification path to requested target
The remote server's TLS certificate isn't trusted by Salesforce. Either the cert is self-signed, signed by a CA Salesforce doesn't trust, expired, or the cert chain is incomplete. You upload the missing cert (or its issuing CA) to the org's CA Certificates store.
Also seen asSSLHandshakeException·PKIX path building failed·unable to find valid certification path·MUTUAL_AUTHENTICATION_FAILED
Salesforce maintains an internal trust store of certificate authorities for outbound HTTPS callouts. If the remote endpoint's cert chain doesn't terminate in one of those CAs, Apex refuses the connection.
Why this fires
Three causes, in order of frequency:
- The remote server uses a self-signed cert. Common for staging/sandbox environments of partner systems. Cert isn't signed by any CA at all.
- Cert chain is incomplete. The server presents only its leaf cert, not the intermediate CA cert that links it to the root. Most browsers patch over this with cached intermediates; Salesforce doesn't.
- CA isn't in Salesforce's trust store. Some smaller or regional CAs aren't included by default.
How to confirm what the server is sending
From your laptop, fetch the cert chain:
openssl s_client -connect api.example.com:443 -showcerts < /dev/null 2>/dev/null | \
awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' > chain.pem
The output is the leaf cert plus any intermediates the server sent. If you see only one -----BEGIN CERTIFICATE----- block, that's the smoking gun — the chain is incomplete and you need to fetch the issuing CA cert from your CA's website (Let's Encrypt's intermediate is https://letsencrypt.org/certs/lets-encrypt-r3.pem, for example).
Upload to Salesforce
Setup → Security → Certificate and Key Management → Import from Keystore (or Upload Custom Certificate). The cert goes into your org's trust store as a CA Certificate. Future callouts to anything signed by that CA succeed.
For Named Credentials, a similar interface exists at the credential level if you want a per-credential trust override.
Don't disable cert validation
You can technically disable cert verification on a callout (req.setHeader('UseCustomTrust', 'no') is not a real Salesforce setting, just an example of the kind of bypass people search for). Don't. The whole point of TLS is verifying who you're talking to. A man-in-the-middle compromise is much worse than a deploy delay.
A nuance: MUTUAL_AUTHENTICATION_FAILED
Slightly different — this is when the server requests a client cert from Salesforce and Salesforce doesn't supply one (or supplies one the server rejects). The fix is to set up Mutual TLS under Setup → Certificate and Key Management, generate a client cert, register the public key with the partner.
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:MyMutualTLSCredential/path'); // Named Credential with cert configured
Use Named Credentials for this, not raw setEndpoint — they handle cert pinning automatically.
Cert expiration
Server certs expire (Let's Encrypt every 90 days, paid CAs every 1-2 years). When the remote service forgets to rotate, you get this error suddenly on a previously-working integration. Verify the cert's notAfter date with:
openssl s_client -connect api.example.com:443 < /dev/null 2>/dev/null | openssl x509 -noout -dates
If it expired yesterday, the fix isn't on your side. Tell the partner.
