When you register a schema containing a callback, External Services creates an invocable Apex operation with an automatically generated Apex class. Salesforce creates a callback URL on the asynchronous callout (initial callout) that’s read-only. Create an Apex client that’s capable of handling the callback by using the generated Apex interfaces. The client waits for an asynchronous response from the external system for an extended time (up to twenty-four hours).
See New connected apps can no longer be created in Spring ‘26 for more details.
Here’s some prerequisite reading and configuration to do before you create your own asynchronous callout.
- Review the fictitious schema on which our examples of asynchronous callbacks are based. See Example API Specification With Callback Operation.
- To authenticate and create requests Define an External Credential and a Named Credential.
- To receive the asynchronous response, create a connected app as described in Authorization Through Connected Apps and OAuth 2.0. Salesforce Platform callback endpoints are exposed with TLS in Connect API. The caller must authenticate to the Salesforce org by setting up the appropriate connected app.
- Register the API specification, using the normal workflow as described in Register an External Service.
As a comparison, a synchronous callout typically looks like this:
ExternalService.AcmeMortgages acme = new ...();
ExternalService.AcmeMortgages.GetApplication_Request request = new ...();
request.applicationNumber = 'X202290';
ExternalService.AcmeMorgages.GetApplication_Response response =
acme.GetApplication(request);
ExternalService.AcmeMortgages_MortgageApplication application = response.Code200;
To invoke an asynchronous callout, you must write your own Apex code to make the callout. Then, you must implement your own callback object and pass it into your asynchronous invocation method within your Apex code.
The callback handler must be stateless. Salesforce doesn't preserve state information when the response is called back.
Don't nest the callback class. The callback class should implement the callback interface directly. For example:
- Correct: class
MyCallbackextendsES.ServiceName.OperationName_Callback {...} - Not Recommended: class
SomeIntermediateClassextendsES.ServiceName.OperationName_Callback {...}and thenMyCallbackextendsSomeIntermediateClass {...}
- From Setup, in the Quick Find box, enter Apex Classes in the Quick Find box, and then select Apex Classes.
- Find the Apex class that contains the callback interface you want to use.
- Create a callback. Write an Apex callback object that implements the External Services generated callback interface by referencing the corresponding Apex class.
- After you have a callback definition, pass this callback into your asynchronous invocation method within your Apex code.
// The callback gets called with the mortgage application update
// and handles each outcome, for example application is approved, rejected, ...
global class MyMortgageApplicationCallback extends ExternalService.AcmeMortgage.SubmitApplication_Callback {
// Application approved
global override void applicationOutcomeApproved(
List<ExternalService.AcmeMortgages.SubmitApplication_applicationOutcomeApproved_Callback> callbacks) {
ExternalService.AcmeMortgages.SubmitApplication_applicationOutcomeApproved_Callback callback = callbacks.get(0);
if (callback.callbackStatus == CallbackStatus.COMPLETED) {
Double loanAmount = callback.response.body.approvedAmount;
// Create a Contact based on the response ...
}
}
// Application rejected
global override void applicationOutcomeRejected(...) {...}
...
}
public class MyMortgageApplicationProcess {
public void submitApplication(...) {
// Submit mortgage application process
ExternalService.AcmeMortgages acme = new ...();
ExternalService.AcmeMortgages.SubmitApplication_Request request = new ...();
ExternalService.AcmeMortgages_Contact applicant = new ...();
applicant.Name = 'Joe Miller';
applicant.Address = '555 Miller St, CA, Milltown';
request.applicant = applicant;
// Time out if no response after 1 day
DateTime timeout = DateTime.now().addDays(1);
ExternalService.AcmeMorgages.SubmitApplication_Response response =
acme.SubmitApplication(request, new MyMortgageApplicationCallback(), timeout);
// Get back application number immediately
String appNr = response.Code201.applicationNumber;
// Get invocationId from a successful initial response to check status of the background operation
String invocationId = response.invocationId;
...
}
}