Integration Patterns – REST Callouts, Named Credentials & Error Handling

Integrating Salesforce with external systems requires making HTTP callouts. This guide covers REST API calls, using Named Credentials for security, and implementing robust error handling.

1. Basic REST Callout

Making a GET Request


public class WeatherService {
    public static void getWeatherData(String city) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('https://api.weather.com/forecast?city=' + city);
        request.setMethod('GET');
        request.setTimeout(120000);
        
        HttpResponse response = http.send(request);
        
        if (response.getStatusCode() == 200) {
            System.debug('Weather Data: ' + response.getBody());
        } else {
            System.debug('Error: ' + response.getStatus());
        }
    }
}
  

2. Using Named Credentials (Secure!)

Never hardcode API keys or URLs in your code. Use Named Credentials instead.

Setup Named Credential in Salesforce:

Use in Your Code


public class PaymentService {
    public static void processPayment(String paymentId, Decimal amount) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        
        // Use callout named credential
        request.setEndpoint('callout:PaymentAPI/process');
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
        request.setTimeout(120000);
        
        String body = JSON.serialize(new Map<String, Object>{
            'paymentId' => paymentId,
            'amount' => amount
        });
        
        request.setBody(body);
        
        try {
            HttpResponse response = http.send(request);
            handlePaymentResponse(response);
        } catch (Exception e) {
            System.debug('Callout failed: ' + e.getMessage());
        }
    }
    
    private static void handlePaymentResponse(HttpResponse response) {
        if (response.getStatusCode() == 200) {
            System.debug('Payment processed successfully');
        } else if (response.getStatusCode() == 400) {
            System.debug('Bad request: ' + response.getBody());
        } else if (response.getStatusCode() == 401) {
            System.debug('Unauthorized');
        }
    }
}
  

3. Making a POST Request with JSON


public class CRMIntegration {
    public static void syncCustomer(Account acc) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint('callout:CRMSystem/customers');
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
        request.setHeader('Authorization', 'Bearer ' + getAccessToken());
        
        CustomerWrapper customer = new CustomerWrapper(acc);
        String jsonBody = JSON.serialize(customer);
        request.setBody(jsonBody);
        
        HttpResponse response = http.send(request);
        
        if (response.getStatusCode() == 201) {
            acc.External_ID__c = JSON.deserialize(response.getBody(), Map<String, Object>).get('id').toString();
            update acc;
        }
    }
    
    private static String getAccessToken() {
        // Fetch token from cache or request new one
        return Cache.SessionCache.getSessionCache().get('crm_token');
    }
    
    public class CustomerWrapper {
        public String name;
        public String email;
        public String phone;
        
        public CustomerWrapper(Account acc) {
            this.name = acc.Name;
            this.email = acc.Email__c;
            this.phone = acc.Phone;
        }
    }
}
  

4. Error Handling Best Practices

Handling Different HTTP Status Codes


public class RobustIntegration {
    public static void callExternalAPI(String endpoint, String payload) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(endpoint);
        request.setMethod('POST');
        request.setBody(payload);
        request.setTimeout(120000);
        
        HttpResponse response;
        
        try {
            response = http.send(request);
            
            switch on response.getStatusCode() {
                when 200, 201 {
                    handleSuccess(response);
                }
                when 400 {
                    handleBadRequest(response);
                }
                when 401 {
                    handleUnauthorized(response);
                }
                when 404 {
                    handleNotFound(response);
                }
                when 429 {
                    handleRateLimit(response);
                }
                when 500, 502, 503 {
                    handleServerError(response);
                }
                when else {
                    handleUnexpectedError(response);
                }
            }
        } catch (CalloutException e) {
            System.debug('Callout timeout or other error: ' + e.getMessage());
            // Implement retry logic or logging
        } catch (Exception e) {
            System.debug('Unexpected error: ' + e.getMessage());
        }
    }
    
    private static void handleSuccess(HttpResponse response) {
        System.debug('Success: ' + response.getBody());
    }
    
    private static void handleBadRequest(HttpResponse response) {
        System.debug('Bad Request: ' + response.getBody());
        // Log to custom error table
    }
    
    private static void handleUnauthorized(HttpResponse response) {
        System.debug('Refresh credentials and retry');
    }
    
    private static void handleRateLimit(HttpResponse response) {
        System.debug('Rate limited. Implement exponential backoff');
    }
    
    private static void handleServerError(HttpResponse response) {
        System.debug('External system error. Queue for retry');
    }
    
    private static void handleUnexpectedError(HttpResponse response) {
        System.debug('Status: ' + response.getStatusCode() + ' - ' + response.getStatus());
    }
}
  

5. Callout Timeout & Retry Pattern


public class ResilientCallout {
    private static final Integer MAX_RETRIES = 3;
    private static final Integer TIMEOUT_MS = 120000;
    
    public static void callWithRetry(String endpoint, String payload) {
        Integer retries = 0;
        
        while (retries < MAX_RETRIES) {
            try {
                Http http = new Http();
                HttpRequest request = new HttpRequest();
                request.setEndpoint(endpoint);
                request.setMethod('POST');
                request.setBody(payload);
                request.setTimeout(TIMEOUT_MS);
                
                HttpResponse response = http.send(request);
                
                if (response.getStatusCode() == 200) {
                    return;  // Success
                } else if (response.getStatusCode() >= 500) {
                    retries++;
                    // Exponential backoff not applicable in sync context
                }
            } catch (CalloutException e) {
                retries++;
                if (retries >= MAX_RETRIES) {
                    throw new CustomException('Callout failed after ' + MAX_RETRIES + ' attempts');
                }
            }
        }
    }
}

public class CustomException extends Exception { }
  

Key Takeaways