Callouts in Apex: Secure Auth, Non-Blocking UX, and Resilient Retries
Building reliable Salesforce integrations means balancing security, user experience, and resilience. You want secure authentication, non-blocking calls for the UI, and smart retry logic that doesn’t break governor limits.
In this guide, we’ll walk through how to achieve all three using:
-
Named Credentials for configuration-based security
-
OAuth/JWT for headless authentication
-
Continuation for long-running UI callouts
-
Governor-aware retries for robust, scalable recovery
Named Credentials — Configuration-First Security and Simpler Code
Core idea:
Store your base URL and authentication setup in Setup → Named Credentials, then reference it directly in Apex using callout:Your_NC.
This means your Apex code never handles secrets, tokens rotate automatically, and sandbox vs. production endpoints are easy to manage — no code changes required.
Example scenario:
A finance app makes calls to a payment service API. When the API key rotates, you simply update the Named Credential — no redeployment or code edits needed.
Best practices:
-
Keep URLs, paths, and queries in code — but store the host and authentication in the Named Credential.
-
Add timeouts and idempotency keys for safety.
-
Log correlation IDs, not raw tokens.
public with sharing class PaymentsClient {
public class PaymentResponse { public String id; public String status; }
public static PaymentResponse fetch(String paymentId, String correlationId) {
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setTimeout(10000); // 10s
req.setEndpoint('callout:Payments_NC/v1/payments/' +
EncodingUtil.urlEncode(paymentId, 'UTF-8'));
req.setHeader('Accept', 'application/json');
req.setHeader('X-Correlation-Id', correlationId);
HTTPResponse res = new HTTP().send(req);
if (res.getStatusCode() == 200) {
return (PaymentResponse) JSON.deserialize(res.getBody(), PaymentResponse.class);
}
// Surface concise error, keep details in logs
System.debug(LoggingLevel.WARN, 'Payments fetch failed ' + res.getStatus());
throw new CalloutException('Payments API error: ' + res.getStatus());
}
}
OAuth/JWT (JWT Bearer Flow) — Headless, Server-to-Server Authentication
Core idea:
Use an Auth Provider and Named Credential configured with the JWT Bearer flow. Salesforce automatically signs the JWT with your private key and injects the Bearer token into your callouts.
Your Apex code doesn’t need to handle tokens manually — Salesforce manages the authentication behind the scenes.
Example scenario:
A nightly batch pushes invoices to an ERP system. No user intervention is required, and the integration stays secure without passwords.
Best practices:
-
Use JWT Bearer for backend, headless integrations.
-
Keep scopes as limited as possible.
-
Store private keys securely in the Auth Provider — avoid handling them in Apex.
public with sharing class ERPClient {
public static void pushInvoice(Invoice__c inv, String correlationId) {
HttpRequest req = new HttpRequest();
req.setMethod('POST');
req.setEndpoint('callout:ERP_NC/api/invoices');
req.setHeader('Content-Type', 'application/json');
req.setHeader('X-Correlation-Id', correlationId);
req.setBody(JSON.serialize(inv));
HTTPResponse res = new HTTP().send(req);
Integer code = res.getStatusCode();
if (code >= 200 && code < 300) return;
if (code == 409) throw new CalloutException('Invoice already exists');
throw new CalloutException('ERP error: ' + res.getStatus());
}
}

