AL Development

Calling REST APIs From AL: HttpClient and JSON in Business Central

By Central Era TechnologiesPublished 19 September 20264 min read

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.

Central Era TechnologiesWritten by the consultants and developers at Central Era Technologies, who work with Business Central, Dynamics 365 Finance and Operations, Power Platform and Azure. To credit a named author, add one here.

Working on something similar?

If this article touches a project you are planning or a problem you are stuck on, tell us about it. We will reply with practical next steps.