Webhooks with Dynamics 365 Business Central

When integrating external applications with Dynamics 365 Business Central, one of the classical way is to use APIs (standard or custom APIs) and then call the relative endpoints from the external application.

As said in the past, exposing entities by using standard or custom API pages is the recommended way for doing integrations with Dynamics 365 Business Central. By using APIs, the schema of your integration is the following:


Here, an external application calls a Dynamics 365 Business Central API endpoint by using simple HTTP methods and then it receives a JSON response from the ERP.

One of the problem that you can have when integrating external systems is that sometimes the external application needs to react to changes that occours on ERP entities. Imagine for example to have an external application that needs to handle an external processing (for example shipments labels printing) for every sales order that comes from Dynamics 365 Business Central. Instead of pulling the ERP for retrieving orders and their modifications on some scheduled timing (for example every N minutes), could be useful if the ERP notifies directly the external application every time an entity changes.

This is exactly the concept of Webhooks: Webhooks is the way to get notified if an entity changes in Dynamics 365 Business Central:


In Dynamics 365 Business Central, every entity exposed as API supports webhooks natively.

To start working with Webhooks, you need to follow a request/response workflow in order to establish the communication:

  1. You need to register a webhook subscription with the entities you want to be subscribed to by providing a notification url (endpoint where Dynamics 365 Business Central will send the notifications for your subscribed entities).
  2. You need to return a validation token to Dynamics 365 Business Central from your notification url (handshake).
  3. When the subscription is established, you can start receiving notifications and you can then renew or remove the subscriptions as needed.

To show how you can work with Webhooks in Dynamics 365 Business Central, imagine that we have an external system that needs to be notified on every changes that occours on a Sales Order record in Dynamics 365 Business Central.

I’ve deployed on my Azure subscription an Azure Function that receives a JSON body (notification from Dynamics 365 Business Central with the details of the modified entity) and then performs some work as needed by our business scenario. When deployed, the Azure Function has a public URL (endpoint) and this is the notification url that we’ll use when establishing the webhook subscription to the Sales Order entity in our ERP.

NOTE 1: in our calls here we’re using Basic authentication, but you can use also OAuth2 and in this case the TENANTID parameter is not needed.

NOTE 2: In the samples provided I’m connected to my main production environment (called Production) but you can change this name accordingly with the name of the Dynamics 365 Business Central environment you want to use.

Checking subscriptions

As a first step, we call the Dynamics 365 Business Central Subscriptions API in order to check if there are active webhooks subscriptions on our tenant. For this, we need to send a GET http request to the following endpoint:

GET https://api.businesscentral.dynamics.com/v2.0/TENANTID/production/api/v1.0/subscriptions

and the response from the tenant is as follows:


The response is a JSON array of the active webhook subscriptions and in this case it’s empty (no subscriptions established).

Registering a webhook subscription

To register a webhook subscription, we need to send a POST request to the following endpoint, with Content-Type = application/json:

POST https://api.businesscentral.dynamics.com/v2.0/TENANTID/production/api/v1.0/subscriptions
and by passing the following JSON object in the body:

  "notificationUrl": "https://d365bcwebhooks.azurewebsites.net/api/D365BCListener?code=MYFUNCTIONSECRETKEY",

  "resource": "https://api.businesscentral.dynamics.com/v2.0/TENANTID/production/api/v1.0/companies(COMPANYID)/salesOrders",

  "clientState": "SomeSharedSecretForTheNotificationUrl"

  • notificationUrl is the url of my published Azure Function that will receive notifications from Dynamics 365 Business Central. My Azure Function is protected with AuthorizationLevel = Function so the url must provide the access key defined in the Azure Portal for accessing the function.
  • resource is the API address of the Dynamics 365 Business Central resource that we want to subscribe (in this case I want to subscribe the salesOrders API). It’s important to remember that the subscription is per-company, so you need to provide the company id.
  • clientState is optional and can be included as an “opaque key”, enabling the subscriber to verify notifications.

When the POST request is issued to the subscription API, Dynamics 365 Business Central sends a request to the notificationUrl endpoint by passing a validationToken parameter in the query string.

The subscriber (in my case my Azure Function) retrieves this token from the query string and then it sends back to the caller (Dynamics 365 Business Central) a response with a body that contains the validationToken and by setting http status code = 200 (success).

When Dynamics 365 Business Central receives the response, it checks for the validationToken and if all is ok the subscription is established.

This is the returned response that shows that the webhook subscription is activated (with a proper subscriptionId and expirationDateTime):
If I monitor what happens on my Azure Function, I can see that a request is received (see the spike to the Server requests section of the dashboard):

and if I check the detail of the caller (Azure Application Insights is an extremely useful tool for that), I can see that the caller is an application that comes from North Holland (datacenter location of my Dynamics 365 Business Central tenant):


P.S. this is a nice way for knowing the exact location of the datacenter region where your Dynamics 365 Business Central tenant runs.

Receiving notifications

What happens now that my webhook subscription is activated? Dynamics 365 Business Central starts sending notifications to my endpoint every time something happens on the subscribed entities (pushing).

Here we’ve subscribed for receiving notifications about Sales Orders. If I modify a Sales Order in Dynamics 365 Business Central, a notification is sent to my Azure Function (notificationUrl parameter).

This is what you can see from the Azure Portal after modifying something on a Sales Order record:


As you can see from the Azure Function monitor (Server requests diagram), a new HTTP request is arrived to my endpoint:


and if you inspect the incoming request details, you can see that the caller is always a cloud application that comes from the same region as above mentioned (my Dynamics 365 Business Central tenant).

To show the incoming body request (notification details sent from Dynamics 365 Business Central to my Azure Function) I have logged the incoming request body in the Azure Function log. As you can see in the picture below, it contains a JSON with the modified entity (resource url) and its related state change (created, updated, deleted):


Wow… Dynamics 365 Business Central has sent you a notification!

Please always remember that:

  • a notification sent to the subscriber (notificationUrl endpoint) can contain multiple notifications from different entities (subscriptions)
  • Dynamics 365 Business Central will not send a notification immediately when an entity changes (normally it occours some minutes). This is by design in order to permit to send a single notification even though the entity might have changed several times within a few seconds.

Renewing a webhook subscription

Webhooks subscriptions expires in 3 days (default value). They can be renewed by sending a PATCH request to the subscriptions API (as in the following URL) with Content-Type = application/json:

PATCH https://api.businesscentral.dynamics.com/v2.0/TENANTID/production/api/v1.0/subscriptions('SUBSCRIPTIONID')

This is not well documented by Microsoft actually and it’s a source of problems, but remember that in order to successfully renew a webhook subscription, you need to retrieve the @odata.etag value from the response received when you have established the subscription (see above) and then pass this value as a If-Match block in the PATCH request header.

Without this, you will receive an error that says “Could not validate the client concurrency token required by the service. Please provide a valid token in the client request.”.

Remember also that the @odata.etag value must be passed unescaped (so change \” to “) otherwise you will receive an error saying that “Request data is invalid” (HTTP 400 Bad Request error).

Renewing a subscription also updates the @odata.etag value, so be aware of this (you need to save the new value after the renewal).

NOTE for the on-premise version: the duration for a subscription is 3 days as default also for the on-premise version. This value is specified in the CustomSettings.config file under the ApiSubscriptionExpiration entry. In this file there is also a maximum number of subscriptions admitted specified in the ApiSubscriptionMaxNumberOfSubscriptions entry.

Delete a webhook subscription

To delete a webhook subscription, you can send a DELETE http request to the following endpoint:

DELETE https://api.businesscentral.dynamics.com/v2.0/TENANTID/production/api/v1.0/subscriptions('SUBSCRIPTIONID')

Also this is actually not well documented by Microsoft, but to successfully delete a webhook subscription you need to do exactly like the renewing process previously described (If-Match header with the @odata.etag value).

List of webhooks supported entities

To have a list of all the webhooks supported entities, you can send a GET request to the webhookSupportedResources endpoint (please be aware of the endpoint url):

GET https://api.businesscentral.dynamics.com/v2.0/TENANTID/production/api/microsoft/runtime/beta/companies(COMPANYID)/webhookSupportedResources
This is the response for my tenant:


Here you can see all your standard and custom entities exposed as APIs.

As you can see, Webhooks are extremely powerful and permits you to have a “push-based solution”, where is the ERP that calls you when something happens.

From the external application, in order to have a complete workflow always running by code (no human interaction) you need to:

  • Establish the subscription to an entity and save the @odata.etag, subscriptionId and expirationDateTime values.
  • Handle the notifications accordingly to your business needs. On a notification you have the changeType parameter (what happened on your subscribed entity) and the resource endpoint (that you can use to retrieve the entity details).
  • When you’re near to the expirationDateTime value, you need to renew the subscription (by passing the subscriptionId) and save the new @odata.etag and expirationDateTime values.



Comment List