Microsoft Dynamics 365 Business Central provides a comprehensive API framework designed to support modern integration scenarios and digital transformation initiatives. The platform offers multiple API types and web service models, each optimized for specific use cases and integration patterns.
Business Central's API architecture operates on three primary layers:
Standard APIs provide Microsoft-maintained endpoints for common business objects like customers, vendors, items, and sales documents. These APIs follow OpenAPI specifications and include built-in business logic validation.
Custom Web Services allow organizations to expose specific Business Central pages or codeunits as web endpoints, providing flexibility for unique business requirements.
OData Services offer query-based data access with filtering, sorting, and pagination capabilities, particularly useful for reporting and analytics integration.
The standard API set covers core business entities with consistent patterns:
GET /api/v2.0/companies({companyId})/customers
GET /api/v2.0/companies({companyId})/vendors
GET /api/v2.0/companies({companyId})/items
GET /api/v2.0/companies({companyId})/salesOrders
These endpoints include:
Custom web services provide access to specific Business Central functionality:
Page-based web services expose list and card pages:
// Exposing a Customer Card page as web service
page 50100 "Customer API"
{
APIPublisher = 'mycompany';
APIGroup = 'sales';
APIVersion = 'v1.0';
EntityName = 'customer';
EntitySetName = 'customers';
SourceTable = Customer;
DelayedInsert = true;
layout
{
area(Content)
{
field(number; "No.") { }
field(name; Name) { }
field(email; "E-Mail") { }
field(phoneNumber; "Phone No.") { }
}
}
}
Codeunit-based web services expose business logic:
// Custom business logic exposed as web service
codeunit 50100 "Sales Order API"
{
procedure CreateSalesOrderFromQuote(QuoteNo: Code[20]): Code[20]
var
SalesHeader: Record "Sales Header";
SalesQuoteToOrder: Codeunit "Sales-Quote to Order";
begin
SalesHeader.Get(SalesHeader."Document Type"::Quote, QuoteNo);
exit(SalesQuoteToOrder.GetOrderNo(SalesHeader));
end;
procedure CalculateOrderTotal(OrderNo: Code[20]): Decimal
var
SalesLine: Record "Sales Line";
Total: Decimal;
begin
SalesLine.SetRange("Document Type", SalesLine."Document Type"::Order);
SalesLine.SetRange("Document No.", OrderNo);
SalesLine.CalcSums("Amount Including VAT");
exit(SalesLine."Amount Including VAT");
end;
}
Business Central APIs use OAuth 2.0 for secure authentication:
// Authentication setup for Business Central API access
const authConfig = {
authority: 'https://login.microsoftonline.com/{tenantId}',
clientId: '{applicationId}',
redirectUri: '{redirectUri}',
scopes: ['https://api.businesscentral.dynamics.com/user_impersonation']
};
// Token acquisition
async function getAccessToken() {
const authResult = await msalInstance.acquireTokenSilent({
scopes: authConfig.scopes,
account: account
});
return authResult.accessToken;
}
API access control requires careful permission management:
Data protection measures:
Real-time synchronization using webhook notifications:
// Webhook handler for Business Central events
app.post('/webhook/business-central', (req, res) => {
const notification = req.body;
switch(notification.subscriptionType) {
case 'customers':
handleCustomerChange(notification);
break;
case 'salesOrders':
handleSalesOrderChange(notification);
break;
}
res.status(200).send('OK');
});
async function handleCustomerChange(notification) {
const customerId = notification.resource.split('/').pop();
const customer = await fetchCustomerData(customerId);
await updateExternalCRM(customer);
}
Batch processing for high-volume operations:
// Bulk data processing with pagination
async function syncAllCustomers() {
let skipToken = null;
do {
const response = await fetch(
`${baseUrl}/customers${skipToken ? `?$skiptoken=${skipToken}` : ''}`,
{ headers: { 'Authorization': `Bearer ${token}` }}
);
const data = await response.json();
await processBatch(data.value);
skipToken = extractSkipToken(data['@odata.nextLink']);
} while (skipToken);
}
Comprehensive error handling strategy:
class BusinessCentralAPIClient {
async makeRequest(endpoint, options = {}) {
const maxRetries = 3;
let retryCount = 0;
while (retryCount < maxRetries) {
try {
const response = await fetch(endpoint, {
...options,
headers: {
'Authorization': `Bearer ${await this.getToken()}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (response.status === 429) {
// Rate limiting - wait and retry
const retryAfter = response.headers.get('Retry-After') || 60;
await this.delay(retryAfter * 1000);
retryCount++;
continue;
}
if (!response.ok) {
throw new APIError(response.status, await response.text());
}
return await response.json();
} catch (error) {
if (retryCount === maxRetries - 1) throw error;
retryCount++;
await this.delay(Math.pow(2, retryCount) * 1000); // Exponential backoff
}
}
}
}
Efficient data retrieval using OData query parameters:
// Optimized customer query with specific fields and filtering
const query = {
$select: 'number,name,email,blocked',
$filter: "blocked eq false and lastModifiedDateTime gt 2024-01-01T00:00:00Z",
$orderby: 'lastModifiedDateTime desc',
$top: 100
};
const url = `${baseUrl}/customers?${new URLSearchParams(query)}`;
Pagination handling for large datasets:
// Efficient pagination implementation
async function* getAllRecords(endpoint) {
let url = endpoint;
while (url) {
const response = await this.makeRequest(url);
for (const record of response.value) {
yield record;
}
url = response['@odata.nextLink'];
}
}
// Usage
for await (const customer of getAllRecords('/customers')) {
await processCustomer(customer);
}
Intelligent caching to reduce API calls:
class CachedAPIClient {
constructor() {
this.cache = new Map();
this.cacheTimeout = 5 * 60 * 1000; // 5 minutes
}
async getCachedData(key, fetchFunction) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
return cached.data;
}
const data = await fetchFunction();
this.cache.set(key, {
data: data,
timestamp: Date.now()
});
return data;
}
}
Managing data across multiple Business Central companies:
// Multi-company data aggregation
class MultiCompanyClient {
async aggregateCustomerData(customerNumber) {
const companies = await this.getCompanies();
const customerData = [];
for (const company of companies) {
try {
const customer = await this.getCustomer(company.id, customerNumber);
customerData.push({
companyId: company.id,
companyName: company.name,
customerData: customer
});
} catch (error) {
if (error.status !== 404) throw error;
// Customer doesn't exist in this company - continue
}
}
return customerData;
}
}
Webhook subscription management:
// Webhook subscription for real-time updates
async function setupWebhookSubscriptions() {
const subscriptions = [
{ resource: 'customers', notificationUrl: 'https://myapp.com/webhooks/customers' },
{ resource: 'salesOrders', notificationUrl: 'https://myapp.com/webhooks/orders' }
];
for (const subscription of subscriptions) {
await createSubscription(subscription);
}
}
async function createSubscription(subscription) {
const response = await fetch(`${baseUrl}/subscriptions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
resource: subscription.resource,
notificationUrl: subscription.notificationUrl,
changeType: 'created,updated,deleted'
})
});
return response.json();
}
Comprehensive logging and monitoring:
class APIMonitor {
logAPICall(endpoint, method, duration, statusCode) {
const logEntry = {
timestamp: new Date().toISOString(),
endpoint: endpoint,
method: method,
duration: duration,
statusCode: statusCode,
success: statusCode < 400
};
// Send to monitoring system
this.sendToMonitoring(logEntry);
// Alert on errors or slow responses
if (statusCode >= 400 || duration > 5000) {
this.sendAlert(logEntry);
}
}
generatePerformanceReport() {
return {
avgResponseTime: this.calculateAverageResponseTime(),
errorRate: this.calculateErrorRate(),
mostUsedEndpoints: this.getMostUsedEndpoints(),
slowestEndpoints: this.getSlowestEndpoints()
};
}
}
Consistent API design patterns:
Error handling standards:
// Standardized error response format
{
"error": {
"code": "ValidationError",
"message": "The customer number already exists",
"details": [
{
"code": "DuplicateValue",
"target": "number",
"message": "Customer number 'CUST001' is already in use"
}
]
}
}
Comprehensive API testing approach:
// API integration test suite
describe('Business Central Customer API', () => {
test('should create customer with valid data', async () => {
const customerData = {
name: 'Test Customer',
email: 'test@example.com'
};
const response = await apiClient.createCustomer(customerData);
expect(response.name).toBe(customerData.name);
expect(response.number).toBeDefined();
});
test('should handle duplicate customer number error', async () => {
const customerData = {
number: 'EXISTING001',
name: 'Duplicate Test'
};
await expect(apiClient.createCustomer(customerData))
.rejects.toThrow('Customer number already exists');
});
});
Business Central's API framework continues evolving with new capabilities:
The API ecosystem supports increasingly sophisticated integration scenarios:
Understanding Business Central's API and web service capabilities enables organizations to build robust, scalable integrations that support digital transformation initiatives while maintaining data integrity and security standards. The platform's comprehensive API framework provides the foundation for modern business application architectures.
Learn how integrating Microsoft Power Automate with Dynamics 365 Business Central can transform your business operations, automate workflows, and enhance productivity.
Matias Orlando
2023-12-15
Explore the key differences between C/AL and RTC, the improvements brought by RTC, and the challenges faced during the transition in Microsoft Dynamics NAV.
Kery Nguyen
2023-12-15
A comprehensive guide for beginners to develop their first extension in Microsoft Dynamics 365 Business Central using Visual Studio Code, covering everything from setup to deployment.
Kery Nguyen
2023-12-15