In this video, I explore how we can pull telemetry data into Business Central. Check it out:

In this video, Erik demonstrates how to access telemetry data from Application Insights directly within AL code in Business Central. He walks through the entire process of calling the Application Insights REST API, handling authentication via API keys, constructing HTTP requests with JSON bodies, and parsing the JSON response — all from a BC codeunit.
The Big Plan: Integrating Everything into Business Central
Erik is building an integrated solution for his app company, hogar.com, which he runs with his wife. The goal is to have Business Central serve as the central hub that ties together multiple systems:
- AppSource — for leads and download information
- WooCommerce — their webshop running on WordPress
- Application Insights (Telemetry) — to monitor registration key usage, errors, and software performance
- Mailsend — for automated email sending
The technical hurdle tackled in this video is: How can I get telemetry data into AL? The answer lies in Application Insights’ REST API, which provides web service endpoints for querying telemetry data.
The Kusto Query
Before writing any AL code, Erik starts by crafting a query in the Application Insights portal. The query looks at traces from the last day and projects specific columns:
traces
| where timestamp > ago(1d)
| project customDimensions.eventId, customDimensions.companyName, message
This query retrieves the event ID, company name, and message from recent telemetry traces — a simple starting point to prove the concept works.
Application Insights API Documentation
The Application Insights REST API offers two approaches for executing queries:
- GET — pass the query as a URL parameter (but this introduces encoding headaches with special characters)
- POST — pass the query in the request body as JSON (cleaner and easier)
Erik opts for the POST approach. For authentication, rather than setting up full OAuth 2.0, he uses an API key, which can be generated directly in the Application Insights portal under API Access. You’ll need both the Application ID (part of the URL) and the API key (sent as a header).
Building the HTTP Request in AL
Every HTTP call in AL follows a similar pattern, and Erik walks through it step by step. Here’s the complete codeunit:
codeunit 50100 "Telemetry Query thing"
{
procedure RunQuery()
var
Client: HttpClient;
Request: HttpRequestMessage;
Response: HttpResponseMessage;
Headers: HttpHeaders;
Content: HttpContent;
RequestJson: JsonObject;
ResponseTxt: Text;
ResponseJson: JsonObject;
tables: JsonArray;
T: JsonToken;
firstTable: JsonObject;
tablerows: JsonArray;
row: JsonArray;
begin
Request.Method := 'POST';
Request.SetRequestUri(
'https://api.applicationinsights.io/v1/apps/fe398af0-439e-481b-9033-5c3e2a04c5dc/query');
Request.GetHeaders(Headers);
Headers.Add('x-api-key', 'zjfiij0j5xagnx4qu677j61rj2h1jvyvyp7nsjie');
RequestJson.Add('query',
'traces| where timestamp > ago(1d)| project customDimensions.eventId, customDimensions.companyName, message');
Content.WriteFrom(format(RequestJson));
Content.GetHeaders(Headers);
Headers.Remove('Content-Type');
Headers.Add('Content-Type', 'application/json');
Request.Content(Content);
if Client.Send(Request, Response) then begin
if Response.IsSuccessStatusCode() then begin
Response.Content().ReadAs(ResponseTxt);
ResponseJson.ReadFrom(ResponseTxt);
ResponseJson.Get('tables', T);
tables := T.AsArray();
tables.Get(0, T);
firstTable := T.AsObject();
firstTable.get('rows', T);
tablerows := T.AsArray();
tablerows.get(0, T);
row := T.AsArray();
message('%1', format(row));
end else
error('We got %1 errors', Response.HttpStatusCode());
end else
error('Deep trouble!');
end;
}
Let’s break down the key sections of this code.
Setting Up the Request
Five HTTP-related variable types are needed — and Erik notes you’ll typically end up using one of each:
HttpClient— sends the requestHttpRequestMessage— defines what to sendHttpResponseMessage— holds what comes backHttpHeaders— manages request/content headersHttpContent— holds the request body
The method is set to POST and the URI points to the Application Insights query endpoint, which includes the Application ID.
Working with Headers (The Pointer Pattern)
One of the quirkier aspects of AL’s HTTP implementation is how headers work. You cannot set headers on a request — you can only get them:
Request.GetHeaders(Headers);
Headers.Add('x-api-key', 'zjfiij0j5xagnx4qu677j61rj2h1jvyvyp7nsjie');
The Headers variable acts as a pointer to the headers collection on the request. When you call GetHeaders, the variable becomes linked to the request’s headers. Any modifications you make to Headers are automatically reflected in the request. You never need to “set it back.”
The API key is passed via the x-api-key header — this is how Application Insights authenticates API key-based requests.
Building the JSON Body
The request body is straightforward JSON. According to the API documentation, only the query property is required (timespan is optional):
RequestJson.Add('query',
'traces| where timestamp > ago(1d)| project customDimensions.eventId, customDimensions.companyName, message');
Content.WriteFrom(format(RequestJson));
Content Headers vs. Request Headers
This is where things get a bit weird, as Erik explains. AL inherits from .NET a distinction between request headers and content headers. The Content-Type header belongs to the content, not the request. And since HttpContent already has a default Content-Type, you need to remove it first before setting it to application/json:
Content.GetHeaders(Headers);
Headers.Remove('Content-Type');
Headers.Add('Content-Type', 'application/json');
Note that the same Headers variable is reused here — it now points to the content’s headers instead of the request’s headers. This is technically a pointer reassignment and works fine, though Erik acknowledges it can be confusing.
Don’t Forget to Attach the Content!
Erik ran into a classic gotcha during the live coding session: he built the content, configured all the headers, but forgot to actually attach the content to the request. The fix is a single line:
Request.Content(Content);
Without this line, the POST request is sent with an empty body, and the API returns an empty response. It’s an easy mistake to make!
Parsing the JSON Response
The Application Insights API returns a JSON structure with a tables array. Each table contains name, columns, and rows. The rows are arrays of arrays — plain values without property names:
// Response structure:
// { "tables": [{ "name": "...", "columns": [...], "rows": [["val1", "val2", "val3"], ...] }] }
The parsing code navigates this structure step by step using JsonToken as an intermediary:
ResponseJson.Get('tables', T);
tables := T.AsArray();
tables.Get(0, T); // Zero-indexed!
firstTable := T.AsObject();
firstTable.get('rows', T);
tablerows := T.AsArray();
tablerows.get(0, T); // Zero-indexed!
row := T.AsArray();
Erik points out an important AL quirk: AL is normally one-indexed, but JsonArray.Get() is zero-indexed. The first element is at index 0, not 1.
Triggering the Query from the UI
To test everything, Erik adds a simple action to the Item List page:
pageextension 50100 ItemList extends "Item List"
{
actions
{
addfirst(processing)
{
action(Telemetry)
{
Caption = 'Telemetry';
ApplicationArea = all;
trigger OnAction()
var
t: Codeunit "Telemetry Query thing";
begin
t.RunQuery();
end;
}
}
}
}
Debugging Tips
Erik notes a couple of useful debugging observations:
- The
HttpHeadersvariable isn’t inspectable in the AL debugger — it just shows nothing useful - When you first call an external web service from an app, Business Central may pause to prompt you to allow the outbound connection
- Even on a failed HTTP response (like a 500 error),
Response.Contentmay contain a useful error message from the server — so it’s worth reading
Conclusion
The proof of concept works: telemetry data from Application Insights can be pulled directly into Business Central using AL code. While there’s some “JSON gymnastics” involved in parsing the response, the approach is straightforward and practical. For a production implementation, Erik notes you’d want to:
- Move the Application ID and API key into a setup table rather than hardcoding them
- Store the retrieved telemetry data in a BC table for further processing
- Build proper helper functions for parsing different query structures
Erik’s use case is particularly interesting — he’s not querying telemetry from the same BC instance running the code. He’s pulling telemetry from his AppSource apps, using BC as a central integration hub. The same technique works regardless of where the telemetry originates, as long as it’s in Application Insights.