How to Architect a Microsoft Dynamics 365 Business Central API Integration
A technical guide to integrating the Microsoft Dynamics 365 Business Central API. Covers OAuth 2.0 via Azure AD, API Pages vs OData, rate limits, and when to build vs. buy.
If you're researching how to integrate with the Microsoft Dynamics 365 Business Central API, you already know that ERP integrations are notoriously difficult. Moving financial data between systems demands high precision, strict security, and an architecture that can absorb legacy technical debt without buckling.
The payoff justifies the pain. Automating financial reconciliation through ERP integrations can cut processing time by up to 70%, and 89% of companies identify accounting as the most critical ERP function. Getting your Business Central integration right translates directly into deal velocity and retention.
But the risk is real. The top reasons organizations didn't choose ERP cloud solutions were the risk of a security breach (32%), integration concerns (25%), and data loss (19%). That 25% integration-concern figure should grab your attention: a quarter of potential ERP buyers hesitate specifically because connecting systems is painful. 50% of ERP implementations fail on their first attempt.
This guide covers the actual architectural decisions, authentication pitfalls, rate-limit gotchas, and strategic trade-offs you'll face — whether you're building the integration yourself or evaluating third-party tooling.
Understanding the API Surface: API Pages vs. OData Endpoints
Microsoft provides multiple ways to interact with Business Central. Historically, developers relied on OData web services tied directly to the Business Central user interface. That approach is now on a hard deprecation path.
In the 2027 Wave 1 release of Business Central (version 30.0), Microsoft is retiring the option to expose Microsoft-authored pages as OData web service endpoints. Starting with version 26 (2025 Wave 1), administrators see warnings whenever they try to expose Microsoft UI pages as OData endpoints. SOAP endpoints are blocked by default through feature management in version 26, and they will be fully removed in version 29 (2026 Wave 2).
The deprecation timeline is aggressive:
| Version | Release | Change |
|---|---|---|
| 26 | 2025 Wave 1 | Warnings on OData UI page exposure; SOAP blocked by default |
| 29 | 2026 Wave 2 | SOAP endpoints fully removed |
| 30 | 2027 Wave 1 | OData on Microsoft-authored UI pages removed |
API Pages are Business Central's purpose-built, versioned REST endpoints designed for programmatic access. They replace the legacy pattern of exposing UI pages as OData web service endpoints. Here's why the distinction matters for your integration:
- Contract Stability: OData endpoints were tightly coupled to the UI. If a Business Central administrator renamed a field on a screen, the OData endpoint broke. API Pages are decoupled, contract-based endpoints designed for system-to-system integration.
- Performance: API Pages usually work 30-50% faster. They bypass the heavy UI rendering logic, resulting in significantly lower latency for read/write operations.
- Versioning: API Pages follow a strict versioning scheme (e.g.,
v2.0), so underlying platform updates are far less likely to break your integration logic.
The OData endpoints in Dynamics 365 Business Central expose underlying UI pages as APIs. However, these pages were originally designed for user interface interaction and not programmatic data access. As a result, using OData in this way often leads to decreased performance, inconsistent behavior, and a higher risk of breaking changes when UI pages are modified in updates.
If you're starting a new Business Central integration today, build exclusively against the /api/v2.0/ endpoints. There is zero reason to target OData UI page endpoints.
What the endpoint structure looks like
A standard request to fetch customers:
GET https://api.businesscentral.dynamics.com/v2.0/{tenantId}/{environment}/api/v2.0/companies({companyId})/customers
Authorization: Bearer {access_token}The key segments: tenant ID, environment name (e.g., Production or Sandbox), and the company GUID. Every request requires all three — miss one and you'll get cryptic 404s that the docs don't explain well.
Do not hardcode the environment name (like Production). Enterprise customers often run multiple sandbox and production environments. Your integration should dynamically query the /environments endpoint and allow the user to select their target environment during the onboarding flow.
Navigating Authentication with Azure AD and OAuth 2.0
Authentication is often the most complex phase of a Business Central integration. Business Central requires OAuth 2.0 authentication via Microsoft Entra ID (formerly Azure AD). Web Service Access Keys have been deprecated for Business Central Online since 2022 release wave 1 (BC20). You must use OAuth 2.0 authentication for all API access.
There are two OAuth flows you'll work with:
- Authorization Code Flow — for user-delegated access, where your app acts on behalf of a signed-in user. You'll need to request
Financials.ReadWrite.Allandoffline_accessscopes. Theoffline_accessscope is mandatory — it dictates whether Azure AD issues a refresh token. - Client Credentials Flow — for server-to-server (S2S) integrations with no user interaction. Service-to-Service (S2S) authentication is suited for scenarios where integrations are required to run without any user interaction. S2S authentication uses the Client Credentials OAuth 2.0 Flow.
For most B2B SaaS integrations where you need unattended background syncing, the Client Credentials flow is what you want:
curl -X POST \
"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id={clientId}" \
-d "client_secret={clientSecret}" \
-d "scope=https://api.businesscentral.dynamics.com/.default"If you need a complete walkthrough of the Azure portal setup, refer to our step-by-step guide to creating an Azure AD app for Business Central.
The three-platform authentication dance
Business Central's authentication architecture requires coordination between three distinct Microsoft platforms: Azure Portal (Microsoft Entra ID), Business Central Admin Center, and the Business Central environment itself. This three-platform dance is where most teams lose time:
- Azure App Registration — Register your app, get Client ID and Secret, and configure API permissions (
Dynamics 365 Business Central>API.ReadWrite.Allfor S2S). - Business Central AAD App Card — Inside the BC environment, create a Microsoft Entra Application record with the Client ID and assign permission sets. Note: you cannot assign the 'SUPER' permission set to an application.
- Admin Consent — For multi-tenant apps, tenant admins must grant consent before your app can access their environment. Use the
commonendpoint for multi-tenant authorization:https://login.microsoftonline.com/common/oauth2/v2.0/authorize
Once the user authenticates, extract their specific tenant_id from the token payload and store it. All subsequent API calls must route through their specific tenant and environment.
The OAuth token lifecycle in production
An Access Token is a short-lived credential issued by Microsoft Entra ID after successful authentication. It is a JSON Web Token (JWT) that contains encoded claims such as permissions, user identity, and expiry time. Tokens typically expire after one hour.
That one-hour expiry means tokens will expire mid-sync if you're pulling large datasets. Your backend must maintain a reliable state machine to handle token refreshes:
sequenceDiagram
participant Worker as Background Job
participant DB as Credential Store
participant AD as Azure AD
participant BC as Business Central
Worker->>DB: Fetch credentials near expiry
Worker->>AD: POST /token (grant_type=refresh_token)
AD-->>Worker: New Access & Refresh Tokens
Worker->>DB: Persist new tokens
Worker->>BC: Execute API RequestManaging this lifecycle in production introduces specific race conditions. If two concurrent background jobs attempt to refresh the same token simultaneously, Azure AD may invalidate the entire token chain, forcing the user to manually re-authenticate. Implement distributed locks when refreshing credentials to prevent this — use your database or an in-memory lock to ensure only one process performs the refresh at a time.
If you're running a multi-tenant SaaS product, you're managing refresh cycles for every customer's Business Central environment — each with its own tenant ID, client secret rotation schedule, and permission configuration. For a deep dive into this pattern, review our guide on handling OAuth token refresh failures in production.
Handling Dynamics 365 Business Central API Rate Limits
Microsoft protects its shared infrastructure aggressively. If your application polls Business Central too frequently or attempts bulk data extraction without throttling, you will hit hard walls.
Rate limits are introduced to ensure that no single client consumes too many resources and becomes the noisy neighbor. If calling clients receive an HTTP Status Code 429 - Too Many Requests from Dynamics 365 Business Central, the API request limits are exceeded.
Here's the full picture from Microsoft's operational limits:
| Limit | Production | Notes |
|---|---|---|
| Rate (per user, 5-min window) | 6,000 requests | HTTP 429 when exceeded |
| Max concurrent requests | 5 | 503 when exceeded (queued for up to 8 min) |
| Max connections | 100 | HTTP 429 when exceeded |
| Max request queue size | 95 | HTTP 429 when exceeded |
| Operation timeout | 8 minutes | HTTP 408 returned, session canceled |
| Max webhook subscriptions | 200 | Per environment |
Sandbox environments have lower limits, so don't rely on sandbox testing to validate your production throughput.
Implementing retry logic with exponential backoff
Handling Status Code 429 requires the client to adopt a retry logic while providing a cool off period. Different strategies such as regular interval retry, incremental intervals retry, exponential back-off, or even randomization can be applied.
Your HTTP client must intercept 429 errors and read the Retry-After header. This header tells you exactly how many seconds to wait before making another request. Ignoring it and continuing to hammer the API will result in longer penalty windows or temporary blocks.
async function fetchWithRetry(
url: string,
options: RequestInit,
maxRetries = 5
): Promise<Response> {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) return response;
// Parse Retry-After header if present, else exponential backoff with jitter
const retryAfter = response.headers.get('Retry-After');
const waitMs = retryAfter
? parseInt(retryAfter) * 1000
: Math.min(1000 * Math.pow(2, attempt) + Math.random() * 500, 30000);
console.warn(`Rate limited. Waiting ${waitMs}ms before retry (attempt ${attempt + 1}).`);
await new Promise(resolve => setTimeout(resolve, waitMs));
}
throw new Error('Rate limit exceeded after max retries');
}Beyond retries: webhooks and workload distribution
For bulk data synchronization, relying on retries alone is insufficient. You need to shift from a polling architecture to an event-driven model.
Business Central supports webhook subscriptions for standard entities like Customers, Items, Sales Invoices, and Vendors. Subscribing to webhooks ensures your system only requests data when a change actually occurs, keeping you well below the rate-limit threshold. Two things to watch out for: there's a hard cap of 200 webhook subscriptions per environment, and subscriptions expire and must be periodically renewed.
The real pain emerges when you're syncing data for multiple customers through a single integration. Each customer's Business Central environment has its own rate-limit bucket, but if your app uses a single service principal per tenant, all your requests share that one bucket. Microsoft's own documentation confirms you can increase throughput by distributing requests across multiple users or service principals in a round-robin pattern.
For a broader playbook on rate-limit handling across multiple third-party APIs, see our rate limit best practices guide.
Build vs. Buy: The Hidden Costs of In-House ERP Integrations
Let's be honest about what you're signing up for if you build this integration yourself.
The initial build — setting up the Azure AD app, writing the OAuth handler, and mapping a few API Pages — might take a senior engineer two to four weeks. A basic read-only integration can ship in that window. But a production-grade integration with full OAuth management, rate limiting, error handling, pagination, and webhook support typically takes two to three months. And that's before ongoing maintenance enters the picture.
The real cost is maintenance. When you build in-house, your engineering team permanently owns:
- OAuth lifecycle management across every customer tenant, including secret rotation, consent flows, and re-authentication when tokens are revoked
- Pagination handling for Business Central's OData-style
@odata.nextLinkcursor pagination, which behaves differently from what you might expect if you've worked with offset-based APIs - Schema normalization — Business Central formats data differently than NetSuite, QuickBooks, or Xero. If your product needs to support multiple ERPs, your backend logic becomes a mess of
if/elsestatements handling provider-specific quirks with GUIDs, nested entities likesalesInvoiceLineswithinsalesInvoices, and custom dimensions - Webhook subscription management — subscriptions expire and must be renewed, with the 200-per-environment cap adding hard limits
- API deprecations — Microsoft updates Business Central twice a year (Release Wave 1 and 2). Your team must audit release notes, test against sandbox environments, and refactor code before endpoints are retired
- Edge cases — multi-currency conversions, multi-subsidiary deployments, and custom dimensions that enterprise customers inevitably configure in their ERP
Most ERP projects exceed initial budgets by three to four times. That stat is about ERP implementations broadly, but the pattern holds for integration projects. What starts as a "two-sprint project" turns into a permanent line item on your engineering backlog.
The calculus shifts dramatically when you consider that Business Central is rarely the only ERP your customers use. If you also need to support QuickBooks, Xero, NetSuite, and Sage Intacct, building and maintaining separate integrations for each one compounds the cost. Every hour your engineers spend debugging a failed Azure AD token refresh is an hour they're not building your core product features.
How Truto Simplifies Business Central Integrations
Truto provides a Unified Accounting API that normalizes Business Central's API Pages into standard resources — Invoices, Customers, Vendors, Payments, Journal Entries — alongside QuickBooks Online, Xero, NetSuite, and other ERPs. You integrate once against Truto's schema, and it works across all supported accounting platforms.
# List invoices from any connected ERP — Business Central, QuickBooks, Xero, etc.
curl -X GET \
"https://api.truto.one/unified/accounting/invoices?integrated_account_id={account_id}" \
-H "Authorization: Bearer {your_truto_token}"The same endpoint, same response schema, regardless of whether the customer's backend is Business Central or NetSuite. The integrated_account_id parameter routes the request to the right provider.
Zero integration-specific code
Truto's architecture uses a declarative mapping configuration to translate unified API requests into provider-specific API calls. There's no custom runtime code for Business Central vs. QuickBooks vs. Xero. When you request an Invoice, Truto's proxy layer automatically translates the normalized request into the specific Business Central API Page format, handles the pagination, and uses JSONata expressions to map the response back into a standard schema.
This means Microsoft's deprecation of OData endpoints or schema changes in a new release wave gets handled as a configuration update on Truto's side — not a code deployment on yours.
Automated token lifecycle management
Truto completely offloads the Azure AD OAuth burden. The platform refreshes tokens shortly before they expire, handling the distributed locking to prevent race conditions. If a token is permanently revoked by a Business Central admin, Truto emits an integrated_account:authentication_error webhook, allowing you to prompt the user to reconnect without writing custom monitoring logic.
Rate-limit management and bulk sync
Requests to Business Central are paced to respect the per-user rate limits, with automatic exponential backoff on HTTP 429 responses. For bulk data sync scenarios — syncing all invoices nightly, for example — Truto's RapidBridge lets you define data pipelines that pull from Business Central and push to your data store. It manages pagination, backoff, and webhook subscriptions internally, so your application receives clean, normalized data without managing the ingestion infrastructure yourself.
A word on trade-offs
Using any unified API means accepting an abstraction layer between you and the raw provider API. If you need deep Business Central-specific features — custom API pages built with AL extensions, bound actions on the v2.0 API, or access to Business Central's Admin Center APIs — a unified API won't cover those out of the box. Truto's Proxy API gives you escape-hatch access to the raw Business Central endpoints when the unified model doesn't cover your use case, but it's worth understanding this boundary upfront.
What Your Integration Architecture Should Look Like
flowchart TB
A[Your Application] -->|Single API call| B[Unified Accounting API]
B -->|Route by<br>integrated_account_id| C{Provider Router}
C -->|OAuth 2.0 + Rate Limiting| D[Business Central<br>API v2.0]
C -->|OAuth 2.0 + Rate Limiting| E[QuickBooks Online]
C -->|OAuth 2.0 + Rate Limiting| F[Xero]
C -->|OAuth 2.0 + Rate Limiting| G[NetSuite]
D --> H[Normalized Response]
E --> H
F --> H
G --> H
H -->|Consistent schema| AThis architecture lets you ship a single integration that covers multiple ERPs from day one, and add new providers without touching your application code.
Making the Right Call for Your Team
If you're evaluating how to integrate with the Microsoft Dynamics 365 Business Central API, here's the decision framework:
- Build direct if Business Central is your only ERP target, you need deep access to custom AL extensions, and you have dedicated integration engineers who can own the OAuth, rate-limit, and deprecation maintenance long-term.
- Use a unified API if you need to support Business Central alongside other ERPs, you want to ship the integration in days instead of months, and you'd rather spend engineering cycles on your core product.
- Regardless of approach, build against API Pages (
/api/v2.0/), not legacy OData UI endpoints. Microsoft's 2027 deprecation deadline is real and approaching fast.
The Business Central API is well-designed for what it is — a cloud ERP with a modern REST surface. The complexity isn't in any single API call. It's in the accumulation of OAuth management, rate-limit handling, pagination, webhook lifecycle, and schema mapping across every customer tenant, compounded across every ERP you need to support. That's the problem worth solving strategically.
FAQ
- What are the rate limits for the Dynamics 365 Business Central API?
- Business Central enforces per-user limits of 6,000 OData requests per 5-minute sliding window, a maximum of 5 concurrent requests, and 100 simultaneous connections. Exceeding these returns HTTP 429 Too Many Requests. Sandbox environments have lower thresholds.
- What is the difference between API Pages and OData in Business Central?
- API Pages are purpose-built REST endpoints optimized for integrations, running 30-50% faster than legacy OData UI page endpoints. Microsoft is deprecating OData UI page endpoints in version 30 (2027 Wave 1), so all new integrations should target API Pages at /api/v2.0/.
- How do I authenticate with the Business Central API?
- Business Central requires OAuth 2.0 via Microsoft Entra ID (Azure AD). For server-to-server integrations, use the Client Credentials flow. For user-delegated access, use the Authorization Code flow with offline_access scope. Access tokens expire after one hour and must be refreshed.
- Does Business Central support webhooks?
- Yes, Business Central supports webhook subscriptions for change notifications on entities like customers, invoices, and vendors. There is a hard limit of 200 webhook subscriptions per environment, and subscriptions expire and must be periodically renewed.
- How long does it take to build a Business Central API integration?
- A basic read-only integration can take 2-4 weeks. A production-grade integration with OAuth management, rate limiting, error handling, pagination, and webhook support typically takes 2-3 months and requires ongoing maintenance for API deprecations and schema changes.