Sooner or later a Business Central extension has to talk to another system: a courier, a payment gateway, an e-invoicing portal, a customer's REST service. AL has what you need built in, with HttpClient for requests and the JSON types for payloads. This article shows a clean pattern, and the details that separate a demo from production code.
A first GET request
codeunit 50130 "CE Rest Client"
{
procedure GetJson(Url: Text; var Result: JsonObject): Boolean
var
Client: HttpClient;
Response: HttpResponseMessage;
Body: Text;
begin
if not Client.Get(Url, Response) then
Error('The request could not be sent: %1', GetLastErrorText());
if not Response.IsSuccessStatusCode() then
Error('The service returned %1 %2.', Response.HttpStatusCode(), Response.ReasonPhrase());
Response.Content().ReadAs(Body);
exit(Result.ReadFrom(Body));
end;
}
Notice that there are two different failure modes. Client.Get returns false if the request could not be sent at all (network problem, blocked request). A request that reached the server and returned an error status still counts as a completed call, so you must check IsSuccessStatusCode() explicitly.
Reading JSON
JsonObject, JsonArray, JsonToken and JsonValue form a small tree API. Values are read by key, and you convert tokens to the type you need:
procedure ReadOrder(Json: JsonObject)
var
Token: JsonToken;
LineToken: JsonToken;
OrderNo: Text;
begin
if Json.Get('orderNo', Token) then
OrderNo := Token.AsValue().AsText();
if Json.Get('lines', Token) then
foreach LineToken in Token.AsArray() do
ProcessLine(LineToken.AsObject());
end;
Always handle a missing key deliberately. A silent fallback to an empty value is how bad data ends up in ledgers.
Sending a POST with a JSON body
procedure PostOrder(Url: Text; AccessToken: Text; Payload: JsonObject): Boolean
var
Client: HttpClient;
Request: HttpRequestMessage;
Response: HttpResponseMessage;
Content: HttpContent;
ContentHeaders: HttpHeaders;
Body: Text;
begin
Payload.WriteTo(Body);
Content.WriteFrom(Body);
Content.GetHeaders(ContentHeaders);
ContentHeaders.Remove('Content-Type');
ContentHeaders.Add('Content-Type', 'application/json');
Request.Method := 'POST';
Request.SetRequestUri(Url);
Request.Content := Content;
Client.DefaultRequestHeaders().Add('Authorization', 'Bearer ' + AccessToken);
if not Client.Send(Request, Response) then
exit(false);
exit(Response.IsSuccessStatusCode());
end;
The Content-Type header is removed before it is added because WriteFrom sets a default one. Current AL versions also offer a SecretText type for tokens, and it is worth using where your target runtime supports it so that secrets are not exposed in debugging or logs.
Authentication and secrets
- Never hard-code secrets. Store credentials in Isolated Storage, or better, retrieve them from a secure store such as Azure Key Vault through a small middleware service.
- OAuth 2.0: the System Application includes an OAuth 2.0 codeunit for acquiring tokens with the client-credentials or authorization-code flow. Cache tokens until near expiry rather than requesting one per call.
- Least privilege: request only the scopes the integration needs.
Allowing HTTP requests in SaaS
In Business Central online, an extension cannot make outbound HTTP calls unless they are allowed for that extension. An administrator enables Allow HttpClient Requests in the extension's settings on the Extension Management page. If a call works in your development sandbox but fails after deployment, check this first.
Timeouts, retries and throttling
- Set an explicit timeout with
Client.Timeout()so a slow service cannot hold a user session. - Retry only failures that are safe to retry, for example 429 and 503 responses, with back-off between attempts. Do not blindly retry non-idempotent POSTs, or you may create duplicates. Use an idempotency key if the API supports one.
- Move long-running or bulk calls to a job queue entry, not a page action, so users are not waiting on external systems.
Logging and error handling
Log the URL (without secrets), status code and a correlation ID for every failed call, ideally to a small log table plus telemetry. Surface a clear, actionable message to the user instead of a raw response body. Good integration support is mostly about being able to answer "what did we send, and what came back?" quickly.
Testing
Wrap HTTP access in an interface so tests can substitute a fake implementation, and you can test your parsing and error handling without a live service. This also makes the code easier to move between environments.
Next steps
If you are exposing your own endpoints as well, read Business Central API vs web services. For designing complete integrations, see our Business Central API integration, integration and Azure services, and to build these skills, the technical training covers HttpClient, JSON and OAuth.
Key takeaways
- HttpClient and the JSON types are all you need for most outbound REST calls.
- Always check the HTTP status code and handle non-success responses explicitly.
- Keep secrets out of code, and use Isolated Storage or a secure store for credentials and tokens.
- In Business Central online, outbound requests from an extension must be allowed for that extension.
