Architecture

Set Up Business Central OAuth Service-to-Service Access with Microsoft Entra

Register a Microsoft Entra app, grant API.ReadWrite.All, add it on the Microsoft Entra Applications page and call Business Central APIs with client credentials, then fix 401 errors.

10 min read
On this page

To call Business Central APIs without a signed-in user, register an application in Microsoft Entra ID, give it the API.ReadWrite.All application permission on Dynamics 365 Business Central with admin consent, and add its client ID on the Microsoft Entra Applications page in each Business Central environment with the permission sets it needs. Your integration then requests a token with the client credentials grant and the scope https://api.businesscentral.dynamics.com/.default, and sends it as a bearer token. Most 401 Unauthorized errors come from a step being missed on one side: no admin consent, the app not added or not enabled in that environment, or a token issued for the wrong resource.

Who this is for and what you will have at the end

This guide is for developers and administrators who build unattended integrations with Business Central online: middleware, scheduled jobs, Azure Functions, data platforms and partner products that read or write through the REST API, custom APIs or web services.

At the end you will have:

  • A Microsoft Entra app registration with a client secret or certificate and consented Business Central application permissions.
  • The application set up in Business Central with least-privilege permission sets.
  • A working token request and API call in PowerShell and curl.
  • A troubleshooting map from AADSTS errors and Business Central 401 responses to their causes.

Service-to-service or user impersonation

Business Central supports two OAuth models for web services:

AreaUser impersonation (delegated)Service-to-service (application)
Sign-inRequires an interactive userNo interactive sign-in
LicenseUser must be licensedApplication account in Business Central, no license needed
Refresh tokenUsed to keep access after first sign-inNot needed; request a new token when it expires
PermissionsThe user's permissionsPermissions assigned to the application account

Service-to-service (S2S) authentication uses the OAuth 2.0 client credentials flow. Delegated flows can be subject to multifactor authentication, which blocks unattended integrations because MFA is required to get the token. The old Resource Owner Password Credentials flow isn't supported in Business Central online since version 23, and web service access keys stopped working online in October 2022, so S2S is the model for anything that runs on a schedule. S2S is available online from version 18.3 for API v2.0, custom APIs and web services, and from version 17.0 for the automation APIs.

Prerequisites

  • A Microsoft Entra account that can register applications and grant tenant-wide admin consent.
  • A Business Central user in each target environment who can open the Microsoft Entra Applications page and assign permission sets.
  • The tenant ID and environment names you will call, for example production and sandbox.
  • A place to store the secret or certificate, such as Azure Key Vault or your platform's secret store.

Step 1: Register the application in Microsoft Entra ID

  1. Sign in to the Microsoft Entra admin center and register a new application.
  2. Give it a unique name that identifies the integration, for example bc-integration-crm-sync. One registration per integration makes auditing and throttling easier to reason about.
  3. Under Supported account types, choose Accounts in this organizational directory only for your own integration, or the multitenant option if you're building a product that many customers consent to.
  4. Leave Redirect URI empty unless you want to grant consent from inside Business Central in Step 3; that option needs a Web redirect URI.
  5. From the Overview page, copy the Application (client) ID and Directory (tenant) ID.

Add a credential

Select Certificates & secrets > New client secret, add a description, choose a duration and select Add. Copy the secret value immediately; it isn't shown again after you leave the page.

For a higher level of assurance, Microsoft Entra ID also accepts a certificate, or a federated credential, instead of a shared secret. With a certificate, the token request replaces client_secret with client_assertion_type set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer and a client_assertion JWT signed by the certificate. Whichever you choose, never put the credential in source code.

Step 2: Grant Business Central application permissions

  1. Select API permissions > Add a permission > Microsoft APIs.
  2. Select Dynamics 365 Business Central.
  3. Select Application permissions, then select the permissions you need and Add permissions:
PermissionTypeUse it for
API.ReadWrite.AllApplicationAPI v2.0, custom APIs, OData and SOAP web services
Automation.ReadWrite.AllApplicationAutomation APIs under /api/microsoft/automation, for example company setup
  1. Select Grant admin consent for your tenant. Application permissions only take effect after an administrator consents; there's no user to consent in this flow.

Only add Automation.ReadWrite.All if the integration really uses the automation APIs. A data integration needs API.ReadWrite.All only.

Step 3: Set up the application in Business Central

The token alone doesn't grant data access. Business Central needs an application account with permissions.

  1. In Business Central, search for Microsoft Entra Applications and open the page.
  2. Select New to open the Microsoft Entra Application Card.
  3. In Client ID, enter the application (client) ID from Step 1.
  4. Fill in Description. If a partner set up the application, include enough partner-identifying information to trace it later.
  5. Set State to Enabled.
  6. Assign permission sets. Applications can't be assigned SUPER. Give the account only what the integration needs; for automation scenarios Microsoft points to the D365 AUTOMATION and EXTEN. MGT. - ADMIN permission sets.
  7. If you didn't grant admin consent in the Microsoft Entra admin center, select Grant Consent and follow the wizard. This only works if you configured a redirect URI on the app registration.

Do this in every environment the integration calls. A common cause of an integration that works in the sandbox but fails in production is that the application account, or its permissions, exists in only one of them.

Step 4: Request a token and call the API

The token endpoint is https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token and the scope is https://api.businesscentral.dynamics.com/.default. The .default suffix tells Microsoft Entra ID to issue a token containing every application permission already granted to the app for that resource.

PowerShell

$tenantId     = "<tenant-id>"
$clientId     = "<application-client-id>"
$clientSecret = "<client-secret>"   # load from a secret store in real code
$environment  = "production"
 
$tokenResponse = Invoke-RestMethod -Method Post `
    -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" `
    -ContentType "application/x-www-form-urlencoded" `
    -Body @{
        grant_type    = "client_credentials"
        client_id     = $clientId
        client_secret = $clientSecret
        scope         = "https://api.businesscentral.dynamics.com/.default"
    }
 
$headers = @{ Authorization = "Bearer $($tokenResponse.access_token)" }
$baseUrl = "https://api.businesscentral.dynamics.com/v2.0/$tenantId/$environment/api/v2.0"
 
$companies = Invoke-RestMethod -Uri "$baseUrl/companies" -Headers $headers
$companies.value | Select-Object id, name
 
$companyId = $companies.value[0].id
(Invoke-RestMethod -Uri "$baseUrl/companies($companyId)/customers?`$top=5" -Headers $headers).value |
    Select-Object number, displayName

curl

TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  --data-urlencode "client_secret=$CLIENT_SECRET" \
  -d "scope=https://api.businesscentral.dynamics.com/.default" | jq -r .access_token)
 
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://api.businesscentral.dynamics.com/v2.0/$TENANT_ID/production/api/v2.0/companies"

The client secret must be URL-encoded in the request body, which is why the curl example uses --data-urlencode for it.

A successful token response contains token_type, expires_in and access_token. Access tokens are short-lived (one hour by default), and the client credentials flow never returns a refresh token: when the token expires, request a new one. Use a token cache, such as the one in the Microsoft Authentication Library (MSAL), rather than requesting a token per call.

The same token works for custom APIs (/api/{publisher}/{group}/{version}) and for OData web services. For Business Central on-premises configured for Microsoft Entra authentication with OpenID Connect, the server's ValidAudiences setting must include https://api.businesscentral.dynamics.com.

Verify the setup

  • Token claims. Decode a test token in a local tool. For an app-only token, the roles claim lists the granted application permissions and should include API.ReadWrite.All. The aud claim identifies the resource the token was issued for and must be Business Central, not Microsoft Graph. Don't build production logic on reading tokens for APIs you don't own; this is a debugging step.
  • Companies call. GET .../api/v2.0/companies returns the companies in the environment.
  • Write test. In a sandbox, create and then delete a test record to confirm the permission sets allow writes.
  • Telemetry. Incoming calls appear as RT0008 web service events with category API, which lets you confirm the integration's traffic and error rates.

Troubleshooting

Errors from the token endpoint

These come back as 400 Bad Request with an error and an error_description that starts with an AADSTS code.

ErrorMeaningFix
AADSTS7000215Invalid client secret is providedThe secret in the request doesn't match a valid secret on the app. Check that you stored the secret's value rather than its ID, or create a new secret
AADSTS7000222The provided client secret keys are expiredCreate a new secret or move to a certificate, then update your secret store
AADSTS700016The application wasn't found in the directory/tenantWrong client ID, or the request went to the wrong tenant. Check both values
AADSTS90002The tenant name wasn't foundWrong tenant ID or domain in the token URL
AADSTS70011 (invalid_scope)The scope requested is invalidUse exactly https://api.businesscentral.dynamics.com/.default; all scopes in one request must be for a single resource
AADSTS500011The resource principal wasn't found in the tenantThe Business Central resource isn't available in the tenant you requested a token from. Confirm the tenant is the one that owns the Business Central environment
AADSTS700027Client assertion failed signature validationThe certificate used to sign the assertion doesn't match the one uploaded to the app registration

401 from Business Central

Business Central returns error code Authentication_InvalidCredentials with the message The server has rejected the client credentials when it doesn't accept the token. Work through these checks in order:

  1. Audience. The token was requested with the Business Central .default scope, not a Microsoft Graph or other scope.
  2. Consent. The roles claim contains API.ReadWrite.All. If it's missing, admin consent wasn't granted; grant it and request a new token.
  3. Application account. The client ID is on the Microsoft Entra Applications page of the environment in the URL, and State is Enabled.
  4. Tenant and environment. The tenant ID or domain in the API URL is the tenant that issued the token, and the environment name is spelled correctly.
  5. On-premises only. ValidAudiences includes https://api.businesscentral.dynamics.com.

Other errors after authentication

  • Authorization_* codes or permission errors. The application account authenticated but lacks permissions for the object or data. Add the missing permission set on the application card; don't reach for broad sets.
  • Works in sandbox, fails in production. Compare the application account, its state and its permission sets between the two environments.
  • 429 Too Many Requests. Operational limits apply to service principals exactly as they do to users, for example 6,000 OData requests per user in a 5-minute sliding window. Add retry with back-off, and spread heavy workloads across several applications if one hits the per-user limits.

Security checklist

  • One app registration per integration, named so its owner is obvious.
  • Certificate or federated credential preferred over a client secret; if you use a secret, record its expiry and rotate it before AADSTS7000222 stops the integration.
  • Only API.ReadWrite.All unless automation APIs are needed, with admin consent recorded.
  • Application account enabled only in the environments it needs, with least-privilege permission sets and never SUPER.
  • Tokens cached and never logged; in AL code that handles tokens, mark the procedure [NonDebuggable].
  • Disable the application card in Business Central, or remove the credential in Microsoft Entra ID, as soon as an integration is retired.

The same identity-first approach applies beyond Business Central; see zero trust enterprise remote access architecture. Once authentication works, the next steps are usually moving off SOAP and replacing polling with webhooks.

References

Questions people ask

Does a Business Central service-to-service app need a license?

No. With service-to-service authentication the request runs as an application account in Business Central, which Microsoft documents as requiring no license. A user-impersonation integration, by contrast, runs as a licensed user who has to sign in interactively.

What scope do I use to get a token for Business Central?

Use https://api.businesscentral.dynamics.com/.default with the client credentials grant against https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token. The .default scope tells Microsoft Entra ID to issue a token with the application permissions already granted to the app.

Why do I get 401 Unauthorized even though I have a valid token?

Business Central also has to know the application. Check that the app is on the Microsoft Entra Applications page of the environment you call, that its State is Enabled, that admin consent was granted so the token carries the API.ReadWrite.All role, and that the token was issued for the Business Central resource.

Can I assign the SUPER permission set to an Entra application in Business Central?

No. Applications can't be assigned SUPER. Assign only the permission sets the integration needs, for example D365 AUTOMATION for automation scenarios, following the least-privilege principle.

Business CentralMicrosoft Entra IDOAuth 2.0App Registration
  1. Multi-tenant SaaS on Entra ID: admin consent, tenant allowlist, app lock

    Register a multi-tenant SaaS app in Microsoft Entra ID, onboard customer tenants through admin consent, enforce a tenant allowlist in token validation and lock service principal credentials with app instance property lock.

    Architecture12 min read
  2. Dataverse Application Users: Server-to-Server Auth with an Entra App

    Register a Microsoft Entra app, create a Dataverse application user with a least-privilege security role, get a client credentials token and call the Web API, then fix common errors.

    Architecture10 min read
  3. Secure APIs in Azure API Management with validate-jwt and Rate Limits

    Protect backend APIs behind Azure API Management by validating Microsoft Entra access tokens with validate-jwt and throttling each client application with rate-limit-by-key and quota-by-key.

    Architecture13 min read