Headers for HTTP calls are very important, but HTTP headers can also be quite confusing in AL, in this video, I show how it’s all connected, check it out:

In this video, Erik walks through the often-confusing world of HTTP headers when making web service calls from AL in Business Central. If you’ve ever tried to set a Content-Type header and received a cryptic .NET error, this one’s for you. Erik explains the critical distinction between request headers and content headers, why they must be applied in different places, and how to properly work with both.
Starting Point: A Simple HTTP POST
Erik begins with a piece of code from an earlier video — a simple web service call that posts data to PostTestServer v2, a handy tool that lets you inspect the exact request your code sent. Here’s the starting code:
procedure CallPstv2()
var
Client: HttpClient;
RequestMessage: HttpRequestMessage;
ResponseMessage: HttpResponseMessage;
Content: HttpContent;
JsonBody: Text;
begin
RequestMessage.Method('POST');
RequestMessage.SetRequestUri('https://ptsv2.com/t/youtube/post');
JsonBody := '{"zest": "123"}';
Content.WriteFrom(JsonBody);
RequestMessage.Content := Content;
Client.Send(RequestMessage, ResponseMessage);
end;
Even though no headers are explicitly set in this code, when Erik inspects the result on the test server, nine headers appear. These are automatically added by the .NET HTTP stack underneath Business Central’s AL runtime. Notably, the Content-Type is set to text/plain — which is a problem if the receiving API expects JSON.
The First Attempt: Adding Content-Type to Request Headers
The natural instinct is to use HttpRequestMessage.GetHeaders() to retrieve the headers object, then add the Content-Type header there:
var
Headers: HttpHeaders;
begin
RequestMessage.GetHeaders(Headers);
Headers.Add('Content-Type', 'application/json; charset=utf-8');
end;
But this immediately throws a .NET exception:
System.InvalidOperationException: Misused header name. Make sure request headers are used with HttpRequestMessage, response headers with HttpResponseMessage, and content headers with HttpContent objects.
This is a raw .NET exception bubbling up through AL — a reminder of how thin the wrapper is between AL and the underlying .NET HTTP library.
The Second Attempt: TryAddWithoutValidation
Erik tries using Headers.TryAddWithoutValidation('Content-Type', 'application/json') instead. This call doesn’t throw an error, but when inspecting the result on the test server, the Content-Type is still text/plain. The header was silently ignored because it was being applied to the wrong object.
The Correct Approach: Content Headers
The key insight is that Content-Type is a content header, not a request header. It must be set on the HttpContent object, not the HttpRequestMessage. Here’s how to do it properly:
var
Headers: HttpHeaders;
begin
Content.GetHeaders(Headers);
Headers.Remove('Content-Type');
Headers.Add('Content-Type', 'application/json; charset=utf-8');
end;
Note the call to Headers.Remove('Content-Type') before adding the new value. This is necessary because the content object already has a default Content-Type of text/plain, and this header does not support multiple values. Without the remove, you’ll get another error:
Cannot add value because header Content-Type does not support multiple values.
After this fix, the test server correctly shows application/json as the content type.
Understanding the .NET Header Classification
The reason for this behavior comes directly from .NET’s HTTP implementation. In the .NET reference source, there is an explicit list of headers classified as content headers:
AllowContent-EncodingContent-LanguageContent-LengthContent-LocationContent-MD5Content-RangeContent-TypeExpiresLast-Modified
These headers can only be used with HttpContent objects. All other headers (like Authorization, custom headers, etc.) belong on the request or response message. Because AL’s HTTP types are thin wrappers around these .NET types, they behave identically — including the same errors and constraints.
Adding Request Headers
For non-content headers, using RequestMessage.GetHeaders() works perfectly:
var
Headers: HttpHeaders;
begin
RequestMessage.GetHeaders(Headers);
Headers.Add('Authorization', 'Bearer my-token-here');
Headers.Add('X-Eriks-Header', 'some-custom-value');
end;
An important thing to understand about how the headers variable works: when you call GetHeaders(Headers), the variable you receive is essentially a pointer to the headers collection inside the request (or content) object. There is no corresponding SetHeaders method — you don’t need one. Any modifications you make to the Headers variable are immediately reflected in the parent object. This is somewhat un-AL-like behavior and is straight from .NET’s reference-type semantics.
Reading Response Headers
Headers also exist on the response side. When you receive a response, you can read headers that the server sent back:
var
Headers: HttpHeaders;
Values: array[10] of Text;
begin
ResponseMessage.Headers(Headers);
Headers.GetValues('Some-Header-Name', Values);
end;
This is useful when APIs return metadata in response headers, such as pagination tokens, rate limit information, or correlation IDs.
Bonus Tip: Avoid Heavy Processing in OnOpenPage
Erik shares an important side note about the OnOpenPage trigger, which he was using to fire the HTTP call for demonstration purposes. The OnOpenPage trigger fires before the UI is initialized, meaning:
- Users won’t see anything on screen while it runs
- You can’t show a progress dialog
- The trigger can fire multiple times when the debugger is attached
The recommendation is to encapsulate data processing in a separate object and open the page with pre-loaded data, giving you the opportunity to show dialogs or progress indicators.
Summary
When working with HTTP headers in AL for Business Central, remember these key points:
- Request headers (like
Authorization, custom headers) go on theHttpRequestMessage— useRequestMessage.GetHeaders() - Content headers (like
Content-Type,Content-Encoding) go on theHttpContent— useContent.GetHeaders() - When setting
Content-Type, remove the existing default value first before adding your own - The
Headersvariable is a reference (pointer) to the headers collection — changes are applied immediately without needing a “set” method - AL’s HTTP types are thin wrappers around .NET, so you may occasionally see raw .NET exceptions — reading them carefully often reveals the solution
This is a common stumbling block for AL developers working with web services, largely because the Business Central documentation doesn’t explicitly list which headers are classified as content headers versus request headers. Keeping the .NET reference source in mind can save you significant debugging time.