Building a callout is half code, half configuration. Set up the Named Credential, write the callout code, write the mock-backed tests, and deploy. The order matters; production deploys fail if the Named Credential is not in the deploying metadata package.
- Create a Named Credential
Setup, Quick Find, Named Credentials. New Named Credential. Set the URL, authentication type (Password, OAuth, AWS Signature), and the External Credential (a separate record that stores the actual secret). Named Credentials replace older Auth Provider plus Remote Site Setting setups.
- Write the callout method
Build the HttpRequest, set the endpoint to callout:Named_Credential_Name/path, set headers and body, call new Http().send(request). Parse the response with JSON.deserialize or a typed wrapper class. Keep the method @AuraEnabled or @InvocableMethod if calling from LWC or Flow.
- Handle errors and retries
Check response.getStatusCode() before parsing. Implement retry logic with exponential backoff for 5xx responses. Log every 4xx to a custom error log object for support visibility. Do not silently swallow errors; surface them in a way operators can find.
- Build the mock test class
Implement HttpCalloutMock with a respond method that returns a hardcoded HttpResponse. Test.setMock(HttpCalloutMock.class, new YourMock()) inside the test. Assert on parsed data and on error paths separately.
- Bulkify and async
If the calling context is a trigger, wrap the callout in @future(callout=true) or a Queueable. Pass record IDs through, query inside the async context, not record objects across the async boundary. Async methods cannot return values to the caller; persist results to a custom object or platform event.
GET, POST, PUT, PATCH, DELETE. PATCH is supported but some older REST endpoints reject it; POST with an X-Method-Override header is the workaround.
Default 10 seconds, configurable up to 120 seconds per callout. The aggregate cap of 120 seconds across all callouts in a transaction is what limits total work.
Anonymous, Basic, OAuth, Mutual TLS, AWS Signature, custom. Configured on the Named Credential, not in code. Switching auth mode does not require code changes.
@future(callout=true), Queueable, batch Apex, or Continuation. Pick based on need for state, chaining, or user-facing latency.
- Synchronous callouts cannot fire from a trigger. Wrap in @future(callout=true), Queueable, or platform events.
- Test classes need HttpCalloutMock. Real callouts in tests fail with Methods defined as TestMethod do not support Web service callouts.
- The 120-second aggregate transaction timeout caps total callout time, even if each individual callout finishes within its own timeout.
- Hardcoded endpoints break in sandbox refreshes. Use Named Credentials with environment-specific URLs and refresh-aware metadata.
- Apex callouts require HTTPS in production unless explicitly exempted. HTTP-only endpoints have to be wrapped by a Heroku or middleware proxy.