16.1 HttpClient, HttpRequestMessage & REST API Consumption

Key Takeaways

  • AL provides five foundational HTTP classes for REST integration: HttpClient (client transport engine), HttpRequestMessage (request envelope), HttpResponseMessage (response container), HttpHeaders (header collections), and HttpContent (entity payload and MIME headers).
  • HttpClient provides shorthand helper methods for GET, POST, PUT, and DELETE; however, HTTP PATCH requests require explicitly configuring an HttpRequestMessage with Method('PATCH') and dispatching via HttpClient.Send().
  • Request-level headers (such as Authorization, Accept, and X-API-Key) must be attached to HttpRequestMessage.GetHeaders(), whereas entity/payload headers (such as Content-Type and Content-Encoding) must be attached to HttpContent.GetHeaders().
  • Robust integration error handling requires a two-tiered evaluation: testing the boolean return of HttpClient.Send() for transport/network/timeout failures, followed by evaluating HttpResponseMessage.IsSuccessStatusCode() for 2xx HTTP response status.
  • In Business Central SaaS environments, outbound HTTP calls are blocked by default for newly deployed extensions until a tenant administrator enables the 'Allow HttpClient Requests' toggle on the Extension Management card.
Last updated: August 2026

16.1 HttpClient, HttpRequestMessage & REST API Consumption

Modern enterprise resource planning (ERP) architectures require Microsoft Dynamics 365 Business Central to communicate bidirectionally with external third-party software-as-a-service (SaaS) applications, payment gateways, logistics carriers, tax calculation engines, ecommerce storefronts, and internal microservices. In AL, developers consume external Representational State Transfer (REST) web services using a suite of native HTTP data types modeled closely after the .NET System.Net.Http namespace.

For the MB-820 (Microsoft Dynamics 365 Business Central Developer Associate) examination, developers must master the five core AL HTTP classes, understand the strict distinction between general request headers and content-specific MIME headers, configure request timeouts, handle complex HTTP verbs such as PATCH, implement two-tiered error handling, stream large binary and text payloads, and manage cloud sandbox network security permissions.


1. The AL HTTP Object Model & Architecture

The AL HTTP framework consists of five core reference types that encapsulate the client transport engine, message definitions, headers, and entity payloads:

+-----------------------------------------------------------------------+
|                         AL HTTP ARCHITECTURE                          |
+-----------------------------------------------------------------------+
                                                                         
  [ HttpClient ]                                                         
       │  - Timeout (Default: 100,000 ms / 100 seconds)                  
       │  - Clear() / DefaultRequestHeaders()                            
       │  - Get() / Post() / Put() / Delete() / Send()                   
       │                                                                 
       ▼ Dispatches                                                      
  [ HttpRequestMessage ]                                                 
       ├── .SetRequestUri('https://api.contoso.com/v1/shipments')        
       ├── .Method('POST' | 'GET' | 'PATCH' | 'PUT' | 'DELETE')          
       ├── .GetHeaders(RequestHeaders) ──► [ HttpHeaders ]               
       │                                       - Authorization: Bearer   
       │                                       - Accept: application/json
       │                                       - X-API-Key: secret123    
       └── .Content(HttpBody)                                            
                │                                                        
                ▼                                                        
           [ HttpContent ]                                               
                ├── .WriteFrom(JsonPayloadText | InStream)               
                └── .GetHeaders(ContentHeaders) ──► [ HttpHeaders ]      
                                                       - Content-Type    
                                                       - Content-Length  
                                                                         
       ▲ Receives                                                        
       │                                                                 
  [ HttpResponseMessage ]                                                
       ├── .HttpStatusCode (Integer, e.g., 200, 201, 400, 404, 500)      
       ├── .IsSuccessStatusCode() (Boolean: True if 200..299)            
       ├── .ReasonPhrase (Text, e.g., 'OK', 'Not Found')                 
       ├── .Headers ──► [ HttpHeaders ] (Response Headers)               
       └── .Content ──► [ HttpContent ]                                  
                └── .ReadAs(ResponseText | InStream)                     

The Five Core HTTP Classes

AL ClassDescription & Core ResponsibilitiesKey Methods & Properties
HttpClientThe client transport engine responsible for managing network sockets, SSL/TLS handshake negotiation, applying client timeouts, and dispatching HTTP requests over the network.Get(Uri, HttpResponseMessage)<br/>Post(Uri, HttpContent, HttpResponseMessage)<br/>Put(Uri, HttpContent, HttpResponseMessage)<br/>Delete(Uri, HttpResponseMessage)<br/>Send(HttpRequestMessage, HttpResponseMessage)<br/>Timeout(Integer)<br/>DefaultRequestHeaders(HttpHeaders)<br/>Clear()
HttpRequestMessageRepresents an outbound HTTP request package. Encapsulates the target endpoint URI, HTTP verb method, request-level headers, and entity payload.SetRequestUri(Text)<br/>GetRequestUri(Text)<br/>Method(Text)<br/>GetHeaders(HttpHeaders)<br/>Content(HttpContent)
HttpResponseMessageContainer for the inbound HTTP response returned by the remote server, including HTTP status codes, status descriptions, response headers, and body payload.HttpStatusCode()<br/>IsSuccessStatusCode()<br/>ReasonPhrase()<br/>Content(HttpContent)<br/>Headers(HttpHeaders)
HttpHeadersRepresents a key-value collection of RFC-compliant HTTP headers. Used interchangeably for request headers, response headers, and content-specific entity headers.Add(Text, Text)<br/>TryAddWithoutValidation(Text, Text)<br/>GetValues(Text, Array of [Text])<br/>Contains(Text)<br/>Remove(Text)<br/>Clear()
HttpContentRepresents the entity body (payload) and associated MIME/content headers of an HTTP request or response.`WriteFrom(Text

2. Constructing and Sending REST Requests

When consuming REST APIs in AL, developers can execute requests using high-level shorthand methods on HttpClient or by constructing a full HttpRequestMessage for complete control over HTTP verbs, authentication headers, and request configuration.

General Request Headers vs. Content Headers (Crucial Exam Concept)

A frequent point of failure and a primary focus of the MB-820 exam is the strict separation between Request Headers and Content Headers:

  • Request Headers: Describe the client, authorization, and negotiation parameters. Examples include Authorization, Accept, User-Agent, If-Match, Prefer, and custom headers like X-API-Key or X-Correlation-ID. These headers must be retrieved from and added to HttpRequestMessage.GetHeaders(RequestHeaders) or HttpClient.DefaultRequestHeaders(DefaultHeaders).
  • Content Headers: Describe the entity payload format, size, and encoding. Examples include Content-Type (e.g., application/json, application/xml), Content-Length, Content-Encoding, and Content-Disposition. These headers must be retrieved from and added to HttpContent.GetHeaders(ContentHeaders).

Exam Warning: If you attempt to add Content-Type or Content-Length to the HttpRequestMessage headers collection, Business Central will either throw a runtime exception or silently omit the header during serialization. As a result, the external REST API will reject the request with HTTP 415 (Unsupported Media Type) or HTTP 400 (Bad Request).

Shorthand Methods vs. HttpClient.Send()

HttpClient provides convenience shorthand methods for simple scenarios where custom request headers are minimal:

  • HttpClient.Get(RequestUri: Text, var Response: HttpResponseMessage): Boolean
  • HttpClient.Post(RequestUri: Text, Content: HttpContent, var Response: HttpResponseMessage): Boolean
  • HttpClient.Put(RequestUri: Text, Content: HttpContent, var Response: HttpResponseMessage): Boolean
  • HttpClient.Delete(RequestUri: Text, var Response: HttpResponseMessage): Boolean

However, for enterprise integration where requests require authentication tokens, custom telemetry headers, or the PATCH HTTP verb, developers must construct an HttpRequestMessage and dispatch it using HttpClient.Send(HttpRequestMessage, HttpResponseMessage): Boolean.

Exam Watchout — HTTP PATCH in AL: Notice that HttpClient does not have a HttpClient.Patch() shorthand method. To issue an HTTP PATCH request in AL, you must instantiate an HttpRequestMessage, invoke HttpRequestMessage.Method('PATCH'), attach your HttpContent, and call HttpClient.Send().

Configuring Client Timeout

By default, HttpClient requests wait up to 100,000 milliseconds (100 seconds) before timing out. In synchronous UI actions (such as a user pressing a button on a card page to validate a tax ID or calculate freight rates), a 100-second freeze severely degrades user experience. Developers should explicitly set the timeout duration using the Timeout method (measured in milliseconds):

// Set client timeout to 15,000 milliseconds (15 seconds)
Client.Timeout(15000);
Loading diagram...
AL HTTP Request Pipeline & SaaS Permission Enforcement

3. Production AL Pattern: Secure REST Call with Bearer Authentication

The following codeunit demonstrates a production-ready AL pattern for posting JSON data to an external REST endpoint with OAuth bearer token authentication, proper header placement, timeout configuration, and defensive two-tiered error handling:

codeunit 50120 "REST Integration Management"
{
    Access = Public;

    procedure PostCustomerToExternalApi(CustomerNo: Code[20]; JsonPayload: Text): Boolean
    var
        Client: HttpClient;
        Request: HttpRequestMessage;
        Response: HttpResponseMessage;
        RequestHeaders: HttpHeaders;
        ContentHeaders: HttpHeaders;
        Content: HttpContent;
        ResponseText: Text;
        TargetUri: Label 'https://api.logistics-partner.com/v2/customers/%1', Locked = true;
        BearerToken: Text;
    begin
        // 1. Retrieve authorization token securely from IsolatedStorage
        if not IsolatedStorage.Get('LogisticsApiBearerToken', DataScope::Company, BearerToken) then
            Error('OAuth token not found. Please re-authenticate the logistics service.');

        // 2. Configure entity body and Content-Type header on HttpContent
        Content.WriteFrom(JsonPayload);
        Content.GetHeaders(ContentHeaders);
        if ContentHeaders.Contains('Content-Type') then
            ContentHeaders.Remove('Content-Type');
        ContentHeaders.Add('Content-Type', 'application/json');

        // 3. Configure HttpRequestMessage (URI, Method, and Request Headers)
        Request.SetRequestUri(StrSubstNo(TargetUri, CustomerNo));
        Request.Method('POST');
        Request.Content(Content);

        Request.GetHeaders(RequestHeaders);
        RequestHeaders.Add('Authorization', StrSubstNo('Bearer %1', BearerToken));
        RequestHeaders.Add('Accept', 'application/json');
        RequestHeaders.Add('X-Correlation-ID', Format(CreateGuid()));

        // 4. Configure client timeout (30,000 milliseconds = 30 seconds)
        Client.Timeout(30000);

        // 5. Send request and evaluate Tier 1 transport success
        if not Client.Send(Request, Response) then
            Error('Network communication failure: Unable to establish connection to external endpoint.');

        // 6. Evaluate Tier 2 HTTP response status code
        if not Response.IsSuccessStatusCode() then begin
            Response.Content().ReadAs(ResponseText);
            Error('External API call failed with HTTP Status Code %1 (%2). Server Response: %3',
                Response.HttpStatusCode(),
                Response.ReasonPhrase(),
                ResponseText);
        end;

        // 7. Read and process successful response payload
        Response.Content().ReadAs(ResponseText);
        ProcessSuccessResponse(ResponseText);
        exit(true);
    end;

    local procedure ProcessSuccessResponse(Payload: Text)
    begin
        // Ingest and process response payload
    end;
}

4. Handling Inbound Responses, Stream Buffering & Diagnostic Errors

When HttpClient.Send() executes, the response handling logic must differentiate between two fundamentally distinct failure modes:

  1. Tier 1: Transport / Network Failures (Client.Send returns false):

    • Occurs when the DNS lookup fails, the remote server IP is unreachable, the SSL/TLS certificate handshake is invalid, the connection times out, or the request was blocked by the Business Central SaaS security sandbox.
    • When Send() returns false, no HttpResponseMessage is received from the remote server, and inspecting Response.HttpStatusCode() is invalid.
  2. Tier 2: HTTP Application Failures (Response.IsSuccessStatusCode() returns false):

    • Occurs when network communication was successful and the server replied, but the remote server returned an HTTP error code (4xx client error or 5xx server error).
    • In this case, Send() returns true, and the developer must inspect Response.HttpStatusCode() and read Response.Content() to extract diagnostic error details emitted by the remote API.

Inspecting HTTP Status Codes

  • Response.IsSuccessStatusCode(): Boolean: Returns true if Response.HttpStatusCode() falls within the 200 to 299 range (e.g., 200 OK, 201 Created, 204 No Content).
  • Response.HttpStatusCode(): Integer: Returns the integer status code returned by the server (e.g., 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests, 500 Internal Server Error, 503 Service Unavailable).
  • Response.ReasonPhrase(): Text: Returns the human-readable status description (e.g., 'Unauthorized', 'Not Found').

Reading Content: Text vs. InStream

HttpContent.ReadAs() supports two destination formats:

  • ReadAs(ResponseText: Text): Boolean: Reads the payload directly into an AL string variable. Best suited for JSON and XML payloads of moderate size (< 10 MB).
  • ReadAs(Stream: InStream): Boolean: Reads the raw byte stream directly into an InStream. Mandatory when handling large binary downloads (PDF invoices, shipping labels, Excel files, images, zip files) or very large JSON datasets that would exceed AL string memory limits.
procedure DownloadShippingLabelPdf(LabelUrl: Text; var PdfInStream: InStream)
var
    Client: HttpClient;
    Response: HttpResponseMessage;
begin
    // Set timeout for large file download
    Client.Timeout(60000);

    if not Client.Get(LabelUrl, Response) then
        Error('Failed to establish connection to shipping label server.');

    if not Response.IsSuccessStatusCode() then
        Error('Label download failed with HTTP %1: %2', Response.HttpStatusCode(), Response.ReasonPhrase());

    // Stream binary content directly into InStream without string conversion
    Response.Content().ReadAs(PdfInStream);
end;

5. Cloud & SaaS Outbound HTTP Security & Permission Management

In Dynamics 365 Business Central SaaS (cloud environments), multi-tenant security architecture strictly regulates outbound network traffic originating from AL extensions. By default, newly installed extensions are blocked from making outbound HTTP requests to protect customer data privacy and prevent unintended data exfiltration.

The "Allow HttpClient Requests" Setting

When an extension attempts to invoke HttpClient.Send(), HttpClient.Get(), or any other HTTP method in Business Central SaaS without explicit permission, the platform blocks the request and throws the following runtime error:

"An error occurred while sending the request. The request was blocked by the administrator."

Administrative Configuration Steps

To grant outbound network communication permissions in Business Central SaaS:

  1. Open the Business Central Web Client.
  2. Search for and open the Extension Management page (Page 2500).
  3. Locate and select the extension requiring HTTP communication.
  4. Click Manage -> Configure (or open the Extension Card).
  5. Enable the Allow HttpClient Requests toggle switch.
+-----------------------------------------------------------------------+
|                   EXTENSION MANAGEMENT - EXTENSION CARD               |
+-----------------------------------------------------------------------+
|  Extension Name: Contoso Carrier Logistics Integration                |
|  Publisher:      Contoso Solutions Inc.                               |
|  Version:        24.0.100.0                                           |
|                                                                       |
|  [Settings]                                                           |
|  Deploy Target:                Cloud                                  |
|  Allow HttpClient Requests:    [ X ] Enabled  ◄── (MANDATORY FOR REST)|
+-----------------------------------------------------------------------+

Developer Manifest Considerations (app.json)

While developers cannot force-enable this setting inside app.json (as tenant administrators retain ultimate security authority over outbound traffic), extensions must declare target compatibility:

  • In cloud deployments, "target": "Cloud" ensures that the extension compiles against cloud-safe runtime APIs.
  • For automated installation scripts and CI/CD pipelines, administrators utilize PowerShell cmdlets (on-premises/private cloud: Set-NAVAppSetting -AllowHttpClientRequests) or Admin Center APIs to automate permission toggling during deployment pipelines.
Test Your Knowledge

A developer is writing an AL integration to post customer records to an external REST web service. When sending the POST request, the external service responds with HTTP 415 (Unsupported Media Type). The developer added the header using: HttpRequestMessage.GetHeaders(Headers); Headers.Add('Content-Type', 'application/json');. What is the root cause of this error and how should it be resolved?

A
B
C
D
Test Your Knowledge

A developer needs to send an HTTP PATCH request to update specific fields on an existing customer record in a third-party REST web service. Which approach in AL correctly constructs and sends this request?

A
B
C
D
Test Your Knowledge

An AL procedure executes HttpClient.Send(HttpRequestMessage, HttpResponseMessage) to communicate with an external shipping service. During deployment in Business Central SaaS, the call fails immediately with a runtime error: 'An error occurred while sending the request... The request was blocked by the administrator.' What is the required resolution?

A
B
C
D
Test Your Knowledge

A developer is reviewing error handling in an AL REST integration procedure. Which code pattern correctly differentiates between a network communication failure (e.g., DNS resolution failure or client timeout) and an application-level HTTP error (e.g., HTTP 500 Internal Server Error)?

A
B
C
D