Analyzing Incoming Web Services Request Telemetry

INTRODUCED IN: Business Central 2020 release wave 1

Note

Azure Active Directory is now Microsoft Entra ID. Learn more

Web services telemetry gathers data about SOAP, OData, and REST API requests to the service. It provides information like the request's endpoint, time to complete, HTTP status codes, and more.

General dimensions

The following table explains the general dimensions included in an incoming Web Services Call trace. The table lists the dimensions that are specific to Business Central.

Dimension Description or value
message
  • Web service called (API): {endpoint}
  • Web service called (ODataV4): {endpoint}
  • Web service called (ODataV3): {endpoint}
  • Web service called (SOAP): {endpoint}
severityLevel 1
user_Id The user telemetry ID for the user. From the user card, you can use user_Id to identify the user who triggered this telemetry event. Learn more in Assign a telemetry ID to users.. This dimension was introduced in Business Central 2024 release wave 1, version 24.2.

General dimensions (version 16.0 and earlier)

In versions 16.0 and earlier, the general dimensions look like this:

Dimension Description or value
operation_Name Web Services Call

Note: The use of the operation_Name column was deprecated in version 16.1. In future versions, data won't be stored in this column. So in version 16.1 and later, use the custom dimension column eventID column custom in Kusto queries instead of operation_Name.
message Before version 16.1:
  • Received a web service request of type API
  • Received a web service request of type ODataV4
  • Received a web service request of type ODataV3
  • Received a web service request of type SOAP

Custom dimensions

The following table explains the custom dimensions included in a Web Services Call telemetry event.

For a full KQL example of all dimensions in web services telemetry, see Sample KQL code.

Dimension Description or value
aadTenantId Specifies the Microsoft Entra tenant ID used for Microsoft Entra authentication. For on-premises, if you aren't using Microsoft Entra authentication, this value is common.
alObjectId Specifies the ID of the AL object that was run by the request.[1]
alObjectName Specifies the name of the AL object that was run by the request.[1]
alObjectType Specifies the type of the AL object that was run by the request.[1]
category Specifies the web service type. Values include: API, ODataV4, ODataV3, and SOAP.
companyName The company name in which the request runs.
component Dynamics 365 Business Central Server
componentVersion Specifies the version number of the component that emits telemetry (see the component dimension.)
deprecatedKeys A comma-separated list of all the keys that have been deprecated. The keys in this list are still supported but will eventually be removed in the next major release. We recommend that update any queries that use these keys to use the new key name.
diagnosticsMessage Logged in case of an error in a OData/API call.

This dimension was introduced in Business Central 2023 release wave 1, version 22.0.
endpoint Specifies the endpoint for the request.
environmentName Specifies the name of the tenant environment. See Managing Environments.
environmentType Specifies the environment type for the tenant, such as Production, Sandbox, Trial. See Environment Types
eventId RT0008

This dimension was introduced in Business Central 2020 release wave 1, version 16.1.
extensionId Specifies the appID of the app/extension that the object belongs to.
extensionName Specifies the name of the app/extension that the object belongs to.
extensionVersion Specifies the version of the app/extension that the object belongs to.
extensionPublisher Specifies the publisher of the app/extension that the object belongs to.
failureReason Logged in case of an error in a OData/API request. Contains the exception as seen from the server.

This dimension was introduced in Business Central 2023 release wave 1, version 22.0.
httpHeaders Introduced in version 16.3. Specifies the http headers set in the request. Not logged for SOAP requests. In version 17.3, a truncated version of the Authorization header was introduced to enable querying for the use of basic or token authorization. For more information, see HTTP headers.
httpMethod Introduced in version 16.3. Specifies the HTTP method used in the request. Values include: POST, GET, PUT, PATCH, or DELETE. Not logged for SOAP requests.
httpStatusCode Introduced in version 16.3. Specifies the http status code returned when a request has completed. This dimension further indicates whether request succeeded or not, and why. Use it to verify whether there was an issue with a request even though the request was logged as successful. Not logged for SOAP requests. For more information, see HTTP status codes
queryFilter Specifies the OData/API filter used in the request.
requestQueueTime Specifies the amount of time the request spent in the request queue before it was started by the server.[3]

The time has the format hh:mm:ss.sssssss.
serverExecutionTime Specifies the amount of time it took the server to complete the request**. The time has the format hh:mm:ss.sssssss. Time spent in the request queue isn't included.
sqlExecutes Specifies the number of SQL statements run by the request.[1] [2]
sqlRowsRead Specifies the number of table rows that were read by the SQL statements.[1] [2]
totalTime Specifies the amount of time it took to process the request.[2]

The time has the format hh:mm:ss.sssssss. Time spent in the request queue isn't included.
telemetrySchemaVersion Specifies the version of the Business Central telemetry schema.

1This dimension isn't relevant for $metadata calls, like https://localhost:7048/BC/ODataV4/$metadata, so it won't be in the trace.

2From telemetrySchemaVersion 0.6 and onwards, this value also includes the CompanyOpen operation.

3This dimension was introduced in Business Central 2023 release wave 1, version 22.0.

4This HTTP status code was introduced in Business Central 2023 release wave 1, version 22.2.

HTTP status codes

When you call a web service endpoint, either a Business Central API or from AL using Httpclient datatype, you get an HTTP status code as part of the response. All HTTP status codes that start with 4 (sometimes also written 4xx) are classified as client errors and it is your responsibility to react on these errors and fix them in your code.

In the following table, we list some common 4xx HTTP status codes and suggestions to how to fix them:

HTTP status code Short name Description Suggested solution(s)
400 Bad Request This status code indicates that the server can't or won't process the request due to an error on the client side. For example, it could be a malformed request syntax, header too long, or something else. The client code that calls the endpoint needs to fix things on their end.

For an incoming call of category OData/API, consider using telemetry to find the error. You can also set up a debugger and debug the endpoint code.

For an outgoing call, you need to review/debug the AL code that sends the request.
401 Access denied The request failed because it lacks valid authentication credentials for the target resource. For an incoming call, this is an authorization issue on the Business Central end of this, either in the OnOpenCompany trigger or a permission issue.

For an outgoing call, you need to examine the AL code that sets up certificates or sets authorization header(s).
402 Payment Required Indicates that the caller must make a payment to access the requested resource. Typically used in situations where the server requires payment before granting access to the content or service.

This status code isn't returned by the Business Central server for incoming calls.
For an outgoing call, you need to examine the AL code that calls the service and setup error handling to handle this situation.
403 Forbidden This status code is returned when there's some kind of access restriction policy implemented for the requested resource. For an outgoing call, you need to examine the AL code that sets up certificates (see Httpclient.AddCertificate or sets authorization header(s). Or maybe your app shouldn't call the specified endpoint at all.
404 Not found The resource you're calling doesn't exist (anymore?) For an incoming call of category OData/API, this endpoint isn't available in metadata. If the endpoint used to work, the issue might be due to uninstalling or updating an app/extension. For UI pages exposed as OData, check if the page was disabled by an administrator. Note: We recommend that you move to APIs.

For an outgoing call, you need to examine the URI specified in the AL code calling the endpoint and compare it with the endpoints available on the server you call.
405 Method not allowed The HTTP verb used in the request isn't allowed for the specific URI that an HTTP client requested. For an incoming call, you need to examine the HTTP method and compare that with what the endpoint supports. One example could be a client calling an API query (which is read-only) using HTTP POST.
For on-premises environments where SOAP, OData, or APIs have been turned off, any call to such an endpoint will also return 405 (so check your server configurations if you see those).

For an outgoing call, you need to examine the AL code that calls out and compare the Httpclient method used with what the external service offers.
408 Request timed out This status code is returned when the server didn't receive a complete request message within the time that it was prepared to wait. For an incoming call, this is returned by the Business Central server when the request fails to complete within the limits setup in the server configuration. For online environments, the limits are documented here: Operational Limits for Business Central Online. For on-premises environments, you might be able to adjust the server limits to allow more time for requests to complete. For on-premises environments, you need to change the AL code for the endpoint or call break up the request into multiple requests to make them fit within the time limits.

For an outgoing call, you need to investigate the similar limits setup for the external service. Then either change your AL code or break up the request into multiple requests to make them fit within the time limits.
409 Conflict Indicates that the request couldn't be processed because of conflict in the current state of the resource, such as an edit conflict between multiple simultaneous updates. For an incoming call, this happens when another user has already changed the record(s) that the request is trying to modify.

For an outgoing call, you need to look into the documentation for the service you're calling.
410 Gone Indicates that the resource requested was previously in use but is no longer available and won't be available again. Check you client code/configuration and stop calling this endpoint.
412 Precondition Failed The server doesn't meet one of the preconditions that the requester put on the request header fields. You need to examine the code that calls the endpoint and check if the settings of request or content HTTP headers match what the service expects.

For an incoming call, check if you have supplied the required headers as specified in the API documentation, see API (v2.0) for Business Central for details.

For an outgoing call, a common error is to forget to set the Content-Type header (which by default is set to 'text/plain; charset=utf-8'. For more information about content headers, see HttpContent Data Type.)
413 Payload Too Large / Request Entity Too Large The request is larger than the server is willing or able to process. You need to examine the code that calls the endpoint and adjust the size of the data you send.

For OData/API calls, the limit is called Max body size and its current value for BC online is documented here: OData request limits.

For SOAP calls, the limit is called Max message size and its current value for Business Central online is documented here: SOAP request limits
415 Unsupported Media Type The request uses a media type that isn't supported by the server/resource. Could very well be due to wrong Content-Type or Content-Encoding headers in the request.
418 I'm a teapot This HTTP status is exposed as an Easter egg in some services. If you experience this, there's probably nothing else to do than have a nice cup of tea. Then get back to work afterwards refreshed and ready to fix some more HTTP issues.
429 Too Many Requests This status code is returned when you call too aggressively to the server and it asks you to relax a bit. The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers.
502 Bad Gateway This status code is returned when services in the Business Central data plane are temporarily unavailable. If you experience this, please retry your request. Note that the Business Central service adds a retry-after HTTP header when returning HTTP status 502. Use this in your web service client code.

The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers.
503 Service Temporarily Unavailable This status code is returned when the server is down for maintenance or is overloaded. If you experience this, please retry your request. Note that the Business Central service adds a retry-after HTTP header when returning HTTP status 503. Please use this in your web service client code.

The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers.
504 Gateway Timeout This status code is returned when your call takes longer than the timeout threshold defined on the server. The Business Central performance tuning guide explains how you can implement different retry strategies to deal with this type of issue. For more information, see Performance Articles For Developers.

HTTP headers

For privacy and security reasons, the Business Central maintains a list of HTTP headers that are allowed to be emitted to telemetry.

The following HTTP headers are emitted to telemetry (if set in the request):

  • Accept
  • Accept-Charset
  • Accept-Encoding
  • Accept-Language
  • Allow
  • client-request-id
  • Connection
  • Content-Encoding
  • Content-Language
  • Content-Length
  • Content-Type
  • Data-Access-Intent
  • Expect
  • If-Match
  • isolation
  • odata-isolation
  • OData-MaxVersion
  • OData-Version
  • postman-token
  • Prefer
  • request-id
  • return-client-request-id
  • Transfer-Encoding
  • User-Agent
  • x-forwarded-host
  • x-forwarded-path
  • x-forwarded-port
  • x-forwarded-proto

Who is calling web services endpoints?

When calling Business Central web services, you can inject information about the caller into telemetry for REST API and OData calls.

  • Setting the HTTP header UserAgent logs requests with the httpHeaders dimension.

  • Setting the HTTP header client-request-id, logs requests with the httpHeaders dimension and it also sets the "OperationId"/ClientActivity in Application Insights.

This capability means you can use telemetry to see who's calling Business Central REST APIs and OData web services. It also lets you correlate to telemetry from other web services, if you use Azure Application Insights to instrument them.

Who owns the code behind a web services endpoint?

You can use the custom dimensions alObjectId, alObjectName, and alObjectType to identify the object behind the endpoint. To see which app/extension that the object belongs to, use the custom dimensions extensionId, extensionName, extensionVersion, and extensionPublisher.

This KQL code illustrates how you can find the owner of the code behind and endpoint and what code was running. Such information might be useful, should you need to contact the publisher of an app/extension.

// Incoming Web Service Requests - object and extension information
traces
| where timestamp > ago(1d) // change as needed
| where customDimensions has "RT0008"
| where customDimensions.eventId == "RT0008"
| project timestamp
// in which extension/app
, extensionId = customDimensions.extensionId
, extensionName = customDimensions.extensionName
, extensionVersion = customDimensions.extensionVersion
, extensionPublisher = customDimensions.extensionPublisher
, whoWroteTheCode = case(
    customDimensions.endpoint startswith "MS/", 'Microsoft' // metadata calls and calls to company endpoints do not have data in extension* dimensions
  , customDimensions.extensionPublisher
)
// in which object
, alObjectId = customDimensions.alObjectId
, alObjectName = customDimensions.alObjectName
, alObjectType = customDimensions.alObjectType

For a full KQL example of all dimensions in web services telemetry, see Sample KQL code.

What type of web services are called (REST API, OData, or SOAP)?

The custom dimension category hold information about the type of endpoint (REST API, OData, or SOAP) being called.

This KQL code illustrates how you can find which endpoints are being called and which types of endpoints are being used. Such information might be useful, should you want to modernize your use of web services to use REST APIs, which is recommended over using SOAP web services or OData web services based on pages.

// Incoming Web Service Requests - endpoint information
traces
| where timestamp > ago(1d) // change as needed
| where customDimensions has "RT0008"
| where customDimensions.eventId == "RT0008"
| project timestamp
// endpoint information
, category = customDimensions.category // API, ODataV3, ODataV4, or SOAP
, endpoint = customDimensions.endpoint // URI

For a full KQL example of all dimensions in web services telemetry, see Sample KQL code.

Analyze web service call performance using telemetry

As a developer, you use the data to learn about conditions that you can change to improve performance. The following table provides some examples:

Condition Analysis
A web service request results in a long running SQL query Adjust or fine-tune code.
Web service requests to a specific endpoint read more rows than requests to the other endpoints Consider adding filtering to limit the rows that are read.
Fewer API type requests compared with other types With SOAP and OData requests to UI pages, computation resources are used on UI elements that aren't relevant. Instead of exposing normal pages as web service endpoints, use the built-in API pages. API pages are optimized for this scenario.
High number of requests to endpoints that include Power BI This condition may indicate excessive Power BI integration.

For more performance guidelines, see Web service performance

This KQL code illustrates how you can find the performance characteristics of different endpoints.

// Incoming Web Service Requests - performance information
traces
| where timestamp > ago(1d) // change as needed
| where customDimensions has "RT0008"
| where customDimensions.eventId == "RT0008"
| project timestamp
// endpoint information
, category = customDimensions.category // API, ODataV3, ODataV4, or SOAP
, endpoint = customDimensions.endpoint // URI
// performance data (time is in milliseconds)
, requestQueueTimeMS = toreal(totimespan(customDimensions.requestQueueTime))/10000
, executionTimeInMS = toreal(totimespan(customDimensions.serverExecutionTime))/10000 
, requestTotalTimeMS = ( toreal(totimespan(customDimensions.totalTime))+toreal(totimespan(customDimensions.requestQueueTime)) )/10000

For a full KQL example of all dimensions in web services telemetry, see Sample KQL code.

Analyze web service call stability using telemetry

Business Central telemetry on web service calls have two important dimensions to troubleshoot failed web service calls:

  • The custom dimension httpStatusCode
  • The custom dimension failureReason

For more guidelines on web service call stability, see Troubleshoot web service errors

The custom dimension httpStatusCode

The custom dimension httpStatusCode is key to understanding unsuccessful web service calls. Any call with an HTTP status code in the 4xx range should be investigated because these calls are likely failing due to a misconfiguration on the web service client (the caller).

Business Central online and on-premises are configured with various limits on web service requests. For example, there's a request timeout and a maximum connections limit. For online, you can't change these limits, but it's helpful to know what the limits are. See Current API Limits. For on-premises, you change the limits on the Business Central Server instance. See Configuring Business Central Server. Web service calls that exceed the timeout limit result in a 408 - Request Timeout. These calls are recorded in Application Insights with a totalTime that is equal to the timeout threshold.

For more guidelines on HTTP status codes, see HTTP status codes.

The custom dimension failureReason

The custom dimension failureReason is used to troubleshoot further, mostly error conditions happening in the AL code behind the web service endpoint.

For more information on error codes for the various exceptions that are logged in the custom dimension failureReason, please see Troubleshooting OData/API calls.

Sample KQL code for analyzing failures in web service calls

This KQL code can help you see combinations of httpStatusCode and failureReason in your web service call telemetry:

traces
| where timestamp > ago(60d) // adjust as needed
| where customDimensions has 'RT0008'
| where customDimensions.httpStatusCode <> 200
| where customDimensions.category <> 'SOAP' // no httpStatusCode or failureReason logged for SOAP calls
| project failureReason = tostring( customDimensions.failureReason )
, httpStatusCode = tostring(customDimensions.httpStatusCode)
| where isnotempty( httpStatusCode )
| distinct httpStatusCode, failureReason

For a full KQL example of all dimensions in web services telemetry, see Sample KQL code.

Sample KQL code

This KQL code unfolds all information from the custom dimensions in your web service call telemetry. Use the code sample as a starting point for you analysis and comment out sections for details that you don'tt need.

// Incoming Web Service Requests
traces
| where timestamp > ago(60d) // change if your retention policy is different than the default
| where customDimensions has "RT0008"
| where customDimensions.eventId == "RT0008"
// use this line and comment out the two lines on RT0008 above if you have data prior to version 16.1 
// | where operation_Name == "Web Services Call" // in a later version of the schema, this field will not be used 
//      or customDimensions.eventId == "RT0008" // starting from version 16.1, the eventId is used to identity signal types
| project timestamp
// in which environment did it happen
, aadTenantId = customDimensions.aadTenantId
, environmentName = customDimensions.environmentName
, environmentType = customDimensions.environmentType
// in which extension/app
, extensionId = customDimensions.extensionId
, extensionName = customDimensions.extensionName
, extensionVersion = customDimensions.extensionVersion
, extensionPublisher = customDimensions.extensionPublisher
, whoWroteTheCode = case(
    customDimensions.endpoint startswith "MS/", 'Microsoft' // metadata calls and calls to company endpoints do not have data in extension* dimensions
  , customDimensions.extensionPublisher
)
// in which object
, alObjectId = customDimensions.alObjectId
, alObjectName = customDimensions.alObjectName
, alObjectType = customDimensions.alObjectType
// endpoint information
, category = customDimensions.category // API, ODataV3, ODataV4, or SOAP
, endpoint = customDimensions.endpoint // URI
// how was the endpoint called?
, httpHeaders = customDimensions.httpHeaders // httpHeaders available from 16.3
, httpMethod = customDimensions.httpMethod   // httpMethod available from 16.3
// diagnostics data (how did it go?)
, httpStatusCode = customDimensions.httpStatusCode // httpStatusCode available from 16.3
, diagnosticsMessage = customDimensions.diagnosticsMessage // Not logged for SOAP calls. diagnosticsMessage available from 22.0
, failureReason = customDimensions.failureReason // Not logged for SOAP calls. failureReason available from 22.0
// performance data
// the datatype for executionTime and requestQueueTime is timespan so need to convert to milliseconds
, executionTime = customDimensions.serverExecutionTime
, requestQueueTime = customDimensions.requestQueueTime // This dimension was introduced in Business Central 2023 release wave 1, version 22.0.
, requestQueueTimeMS = toreal(totimespan(customDimensions.requestQueueTime))/10000
, executionTimeInMS = toreal(totimespan(customDimensions.serverExecutionTime))/10000 
, requestTotalTimeMS = ( toreal(totimespan(customDimensions.totalTime))+toreal(totimespan(customDimensions.requestQueueTime)) )/10000
// these extend lines illustrate how to extract data from the httpHeaders dimension
| extend httpHeadersTmp =  tostring( httpHeaders)
| extend httpHeadersJSON = parse_json(httpHeadersTmp)
| extend msUserAgent = tostring( httpHeadersJSON.['ms-dyn-useragent'] )
| extend httpAuthorization = tostring( httpHeadersJSON.['Authorization'] ) // Authorization header (truncated) available from 17.3

To make it easier to get started using Azure Application Insights with Business Central, samples of KQL code are available in the Business Central BCTech repository on GitHub.

If you want to analyze web service call telemetry from the usage of the Microsoft connector (Power BI, Power Apps, ...), then the query MicrosoftConnectorUsage.kql might be useful.

Web service performance
Troubleshoot web service errors
Web services overview
Telemetry overview