Call external services with the HttpClient data type
You can integrate Business Central apps/extensions with external systems by using the HttpClient data type in your AL code.
The HttpClient data type is simply a wrapper on the .NET class HttpClient.
In this article, you learn how to make HTTP requests using the HttpClient data type and handle responses using the HttpResponseMessage data type.
Set up an external call
The first thing you need to do is to define the HTTP request, that is, which URI to call, set request and content HTTP headers, and choose which HTTP method to use. You do this using the HttpRequestMessage data type.
The following example illustrates ways to prepare the request.
local procedure SendRequest(HttpMethod: Text[6]) ResponseText: Text
var
Client: HttpClient;
HttpRequestMessage: HttpRequestMessage;
RequestHeaders: HttpHeaders;
RequestURI: Text;
Content: HttpContent;
ContentHeaders: HttpHeaders;
begin
RequestURI := 'https://httpcats.com/418.json';
// This shows how you can set or change HTTP content headers in your request
Content.GetHeaders(ContentHeaders);
if ContentHeaders.Contains('Content-Type') then ContentHeaders.Remove('Content-Type');
ContentHeaders.Add('Content-Type', 'multipart/form-data;boundary=boundary');
// This shows how you can set HTTP request headers in your request
HttpRequestMessage.GetHeaders(RequestHeaders);
RequestHeaders.Add('Accept', 'application/json');
RequestHeaders.Add('Accept-Encoding', 'utf-8');
RequestHeaders.Add('Connection', 'Keep-alive');
HttpRequestMessage.SetRequestUri(RequestURI);
HttpRequestMessage.Method(HttpMethod);
// from here on, the method must deal with calling out to the external service.
// see example code snippets below
end;
For more information about content headers, see HttpContent data type.
Run the call
When you have set up the request, it's time to call out using a supported HTTP method.
Important
The call might fail in the Business Central platform before actually reaching the external web service. It's therefore important that you check for this in your AL code. Common error examples are if you have added duplicate request or content HTTP headers, if there are networking/firewall issues, if the URI cannot be resolved with DNS, or if the app/extension has not been configured to allow HttpClient requests in the Extension Settings.
The following example shows how to call an external web service from AL. It also illustrates the error handling you need to setup for handling errors when sending the request.
local procedure GetRequest() ResponseText: Text
var
Client: HttpClient;
Response: HttpResponseMessage;
IsSuccessful: Boolean;
ServiceCallErr: Label 'Web service call failed.';
ErrorInfoObject: ErrorInfo;
begin
// assume you have setup the request here
IsSuccessful := Client.Get('https://httpcats.com/418.json', Response);
if not IsSuccessful then begin
ErrorInfoObject.DetailedMessage := 'Sorry, we could not retrieve the cat info right now.';
ErrorInfoObject.Message := ServiceCallErr;
Error(ErrorInfoObject);
end;
end;
Important
Outbound HTTP calls from apps/extensions are blocked by default and must be approved for each extension, otherwise instead of an external call, the system will display the following error message: The request was blocked by the runtime to prevent accidental use of production services.
To enable outbound HTTP calls, go to the Extension Management page in Business Central, and choose Configure. Then, on the Extension Settings page, make sure that Allow HttpClient Requests is selected. This setting must be enabled for each app/extension, including library apps.
Supported HTTP methods
The HttpClient datatype supports the following HTTP methods:
- DELETE
- GET
- PATCH
- POST
- PUT
The following table includes links to help you get started with calling external services using different HTTP methods.
To | See |
---|---|
Dynamically set the HTTP method from AL, set it on the HttpRequestMessage datatype and use the HttpClient.Send method to call the service. | HttpClient.Send(HttpRequestMessage, var HttpResponseMessage) Method HttpRequestMessage.Method([Text]) Method |
Delete data in a service endpoint using the HTTP DELETE method. | HttpClient.Delete(Text, var HttpResponseMessage) Method |
Read data from a service endpoint using the HTTP GET method. | HttpClient.Get(Text, var HttpResponseMessage) Method |
Update data in a service endpoint using the HTTP PATCH method (no specific AL method exists for PATCH, so use HttpClient.Send). | HttpClient.Send(Text, HttpContent, var HttpResponseMessage) Method |
Send data to a service endpoint using the HTTP POST method. | HttpClient.Post(Text, HttpContent, var HttpResponseMessage) Method |
Send data to a service endpoint using the HTTP PUT method. | HttpClient.Put(Text, HttpContent, var HttpResponseMessage) Method |
Parsing the result
If the HttpClient call succeeds, you get a response back from the service you called. The response is saved in the data type HttpResponseMessage, where you can parse it to use the information in your app. The service call itself might not succeed, so make sure that you check the HTTP status code in your AL code.
The following example illustrates the error handling you need to setup for handling errors from the service that you called.
local procedure GetRequest() ResponseText: Text
var
Client: HttpClient;
IsSuccessful: Boolean;
ServiceCallErr: Label 'Web service call failed.';
ErrorInfoObject: ErrorInfo;
begin
IsSuccessful := Client.Get('https://httpcats.com/418.json', HttpResponseMessage);
if not IsSuccessful then begin
// handle the error
end;
if not HttpResponseMessage.IsSuccessStatusCode() then begin
HttpStatusCode := HttpResponseMessage.HttpStatusCode();
ErrorInfoObject.DetailedMessage := 'Sorry, we could not retrieve the cat info right now. ';
ErrorInfoObject.Message := Format(ServiceStatusErr, HttpStatusCode, HttpResponseMessage.ReasonPhrase());
Error(ErrorInfoObject);
end;
HttpResponseMessage.Content().ReadAs(ResponseText);
end;
HTTP status codes
When you try to access content on a server by using the HTTP protocol, it returns a numeric code that indicates the result of the request and semantics of the response, including whether the request was successful.
The first digit of the status code defines the class of response. The last two digits don't have any categorization role. There are five values for the first digit:
1xx (Informational): Provisional response - the request was received, continuing process.
2xx (Successful): The server successfully received and accepted the request.
3xx (Redirection): Further action needs to be taken in order to complete the request.
4xx (Client Error): The request contains an error and can't be fulfilled.
5xx (Server Error): The server failed to fulfill the request.
For more information, see HTTP Semantics
Common HTTP status error codes
When you call a web service endpoint, either a Business Central API or from AL using Httpclient datatype, you get an HTTP status code as part of the response. All HTTP status codes that start with 4 (sometimes also written 4xx) are classified as client errors and it is your responsibility to react on these errors and fix them in your code.
In the following table, we list some common 4xx HTTP status codes and suggestions to how to fix them:
HTTP status code | Short name | Description | Suggested solution(s) |
---|---|---|---|
400 | Bad Request | This status code indicates that the server can't or won't process the request due to an error on the client side. For example, it could be a malformed request syntax, header too long, or something else. | The client code that calls the endpoint needs to fix things on their end. For an incoming call of category OData/API, consider using telemetry to find the error. You can also set up a debugger and debug the endpoint code. For an outgoing call, you need to review/debug the AL code that sends the request. |
401 | Access denied | The request failed because it lacks valid authentication credentials for the target resource. | For an incoming call, this is an authorization issue on the Business Central end of this, either in the OnOpenCompany trigger or a permission issue. For an outgoing call, you need to examine the AL code that sets up certificates or sets authorization header(s). |
402 | Payment Required | Indicates that the caller must make a payment to access the requested resource. Typically used in situations where the server requires payment before granting access to the content or service. This status code isn't returned by the Business Central server for incoming calls. |
For an outgoing call, you need to examine the AL code that calls the service and setup error handling to handle this situation. |
403 | Forbidden | This status code is returned when there's some kind of access restriction policy implemented for the requested resource. | For an outgoing call, you need to examine the AL code that sets up certificates (see Httpclient.AddCertificate or sets authorization header(s). Or maybe your app shouldn't call the specified endpoint at all. |
404 | Not found | The resource you're calling doesn't exist (anymore?) | For an incoming call of category OData/API, this endpoint isn't available in metadata. If the endpoint used to work, the issue might be due to uninstalling or updating an app/extension. For UI pages exposed as OData, check if the page was disabled by an administrator. Note: We recommend that you move to APIs. For an outgoing call, you need to examine the URI specified in the AL code calling the endpoint and compare it with the endpoints available on the server you call. |
405 | Method not allowed | The HTTP verb used in the request isn't allowed for the specific URI that an HTTP client requested. | For an incoming call, you need to examine the HTTP method and compare that with what the endpoint supports. One example could be a client calling an API query (which is read-only) using HTTP POST. For on-premises environments where SOAP, OData, or APIs have been turned off, any call to such an endpoint will also return 405 (so check your server configurations if you see those). For an outgoing call, you need to examine the AL code that calls out and compare the Httpclient method used with what the external service offers. |
408 | Request timed out | This status code is returned when the server didn't receive a complete request message within the time that it was prepared to wait. | For an incoming call, this is returned by the Business Central server when the request fails to complete within the limits setup in the server configuration. For online environments, the limits are documented here: Operational Limits for Business Central Online. For on-premises environments, you might be able to adjust the server limits to allow more time for requests to complete. For on-premises environments, you need to change the AL code for the endpoint or call break up the request into multiple requests to make them fit within the time limits. For an outgoing call, you need to investigate the similar limits setup for the external service. Then either change your AL code or break up the request into multiple requests to make them fit within the time limits. |
409 | Conflict | Indicates that the request couldn't be processed because of conflict in the current state of the resource, such as an edit conflict between multiple simultaneous updates. | For an incoming call, this happens when another user has already changed the record(s) that the request is trying to modify. For an outgoing call, you need to look into the documentation for the service you're calling. |
410 | Gone | Indicates that the resource requested was previously in use but is no longer available and won't be available again. | Check you client code/configuration and stop calling this endpoint. |
412 | Precondition Failed | The server doesn't meet one of the preconditions that the requester put on the request header fields. | You need to examine the code that calls the endpoint and check if the settings of request or content HTTP headers match what the service expects. For an incoming call, check if you have supplied the required headers as specified in the API documentation, see API (v2.0) for Business Central for details. For an outgoing call, a common error is to forget to set the Content-Type header (which by default is set to 'text/plain; charset=utf-8'. For more information about content headers, see HttpContent Data Type.) |
413 | Payload Too Large / Request Entity Too Large | The request is larger than the server is willing or able to process. | You need to examine the code that calls the endpoint and adjust the size of the data you send. For OData/API calls, the limit is called Max body size and its current value for BC online is documented here: OData request limits. For SOAP calls, the limit is called Max message size and its current value for Business Central online is documented here: SOAP request limits |
415 | Unsupported Media Type | The request uses a media type that isn't supported by the server/resource. | Could very well be due to wrong Content-Type or Content-Encoding headers in the request. |
418 | I'm a teapot | This HTTP status is exposed as an Easter egg in some services. | If you experience this, there's probably nothing else to do than have a nice cup of tea. Then get back to work afterwards refreshed and ready to fix some more HTTP issues. |
429 | Too Many Requests | This status code is returned when you call too aggressively to the server and it asks you to relax a bit. | The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers. |
502 | Bad Gateway | This status code is returned when services in the Business Central data plane are temporarily unavailable. | If you experience this, please retry your request. Note that the Business Central service adds a retry-after HTTP header when returning HTTP status 502. Use this in your web service client code. The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers. |
503 | Service Temporarily Unavailable | This status code is returned when the server is down for maintenance or is overloaded. | If you experience this, please retry your request. Note that the Business Central service adds a retry-after HTTP header when returning HTTP status 503. Please use this in your web service client code. The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers. |
504 | Gateway Timeout | This status code is returned when your call takes longer than the timeout threshold defined on the server. | The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers. |
Advanced scenarios
Using cookies
Starting from 2024 release wave 1, you can use server-side cookies when calling an external service using HttpClient This allows you to efficiently send and receive cookies in HTTP requests, unblocking scenarios where third-party endpoints require cookie customization. With the Cookie datatype and AL methods for handling cookies, you can automatically re-use response cookies, handle cookies manually, or a mix of both.
Using certificates
It's possible to include a certificate when calling an external service using HttpClient.
The following example shows how to add a certificate to the HttpClient data type.
// This code shows how to use certificates with HttpClient
procedure AddCertificateToHttpClient(var HttpClient: HttpClient; CertificateCode: Text[6])
var
IsolatedCertificate: Record "Isolated Certificate";
CertificateManagement: Codeunit "Certificate Management";
begin
if not IsolatedCertificate.Get(CertificateCode) then
exit;
HttpClient.AddCertificate(
CertificateManagement.GetCertAsBase64String(IsolatedCertificate),
CertificateManagement.GetPassword(IsolatedCertificate));
end;
Important
In Business Central versions 22 (2023 release wave 1) and later, certificates must include the following information:
- If KeyUsage is defined, specify DigitalSignature.
- If ExtendedKeyUsage is defined, specify ClientAuthentication.
This is due to different behavior between .NET Core and .NET Framework.
When making an outbound http call to an external endpoint, if you receive a 403 response (external endpoint required a certificate), and your code does have a HttpClient.AddCertificate, check the version of the Business Central platform and the requirements for KeyUsage and ExtendedKeyUsage.
For more information about certificates, see
Which IP addresses or ranges does my environment use?
When you exchange data through external services, you might have to safelist the IP addresses from where the Business Central service is running.
For more information, see
- FAQ: IP addresses or ranges for the Business Central service
- How-to restrict network access from/to Business Central.
Monitor and troubleshoot
Outgoing web service request telemetry gathers data about outgoing web service requests sent using the AL HTTPClient data type. This data gives you insight into the execution time and failures that happen in external services that your environment and extensions depend on. You can also use the data to alert on environments or apps for performance issues caused by external services, and be more proactive in preventing issues from occurring.
Application Insights
You can set up Business Central to send telemetry traces to an Application Insights resource in Azure. Once set up, telemetry data is sent to the resource when web services are called using the HttpClient data type. For more information, see, Analyzing outgoing web service request telemetry.
Troubleshoot errors
When calling external web services using the HttpClient module in AL, make sure that you handle the HTTP status code returned by the service in case of errors. For more information, see HttpHttpResponseMessage.HttpStatusCode() Method.
If you've enabled telemetry for your environment or app, you can use this KQL query to analyze errors in calls to external services.
traces
| where customDimensions has 'RT0019'
| extend httpStatusCode = toint( case( isnotempty(customDimensions.httpStatusCode), customDimensions.httpStatusCode, customDimensions.httpReturnCode ) )
| where httpStatusCode > 299
| summarize count() // how many calls fail
by httpStatusCode
, endpoint = tostring( customDimensions.endpoint ) // which endpoint
Performance considerations
If you call an external web service using the HttpClient module in AL, be aware that the Business Central Server blocks the execution of AL code for the session until the call completes. For interactive sessions, this behavior means that the user sees a spinning wheel during the call.
If you have enabled telemetry for your environment or app, you can use this KQL query to analyze how much time users are delayed in the UI by calls to external services.
traces
| where customDimensions has 'RT0019'
| where customDimensions.clientType == 'WebClient'
| extend executionTimeInMs = toreal(totimespan(customDimensions.serverExecutionTime))/10000 //the datatype for executionTime is timespan
| summarize count() // how many calls
, sum(executionTimeInMs) // sum of delays for UI sessions
, avg(executionTimeInMs) // average waiting time by this app
, max(executionTimeInMs) // average waiting time by this app
by
// which app is calling out from the UI
extensionPublisher = tostring( customDimensions.extensionPublisher )
, extensionName = tostring( customDimensions.extensionName )
, extensionVersion = tostring( customDimensions.extensionVersion )
Related information
Supported cipher suites in HTTPS
How-to restrict network access from/to Business Central
HttpClient data type
HttpContent data type
Analyzing outgoing web service request telemetry
Developing extensions
Get started with AL