Download our free white paper on Copilot in Microsoft Business Central! Download Now

Using APIs and Web Services with Business Central

Kery Nguyen
By Kery Nguyen

2023-10-25

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.

API Types and Their Applications

Standard Business Central APIs

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:

  • Full CRUD operations (Create, Read, Update, Delete) for supported entities
  • Built-in validation enforcing Business Central business rules
  • Consistent error handling with standardized HTTP status codes
  • Automatic audit trails maintaining change history

Custom Web Services Implementation

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;
}

Authentication and Security Implementation

OAuth 2.0 Authentication Flow

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;
}

Security Best Practices

API access control requires careful permission management:

  • Service-to-service authentication using Azure AD application registrations
  • User impersonation scenarios maintaining Business Central user permissions
  • Scope-based authorization limiting API access to necessary operations
  • Rate limiting considerations preventing system overload

Data protection measures:

  • HTTPS encryption for all API communications
  • Field-level security hiding sensitive data based on user permissions
  • Audit logging tracking all API access and modifications
  • IP filtering restricting access from approved network ranges

Practical Integration Patterns

Data Synchronization Workflows

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);
}

Error Handling and Resilience

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
            }
        }
    }
}

Performance Optimization Strategies

Query Optimization

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);
}

Caching Strategies

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;
    }
}

Advanced Integration Scenarios

Multi-Company Data Access

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;
    }
}

Real-time Event Processing

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();
}

Monitoring and Diagnostics

API Performance Monitoring

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()
        };
    }
}

Development Best Practices

API Design Principles

Consistent API design patterns:

  • RESTful endpoint naming following standard conventions
  • Proper HTTP status codes for different response scenarios
  • Versioning strategy to maintain backward compatibility
  • Documentation standards including OpenAPI specifications

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"
            }
        ]
    }
}

Testing Strategies

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');
    });
});

Future Considerations and Evolution

Emerging API Capabilities

Business Central's API framework continues evolving with new capabilities:

  • GraphQL support for more flexible data querying
  • Real-time collaboration through SignalR integration
  • AI and machine learning API endpoints for predictive analytics
  • Enhanced automation through Power Platform integration

Integration Ecosystem Expansion

The API ecosystem supports increasingly sophisticated integration scenarios:

  • Microservices architectures using Business Central as a data source
  • Event-driven architectures with improved webhook capabilities
  • Multi-cloud deployments supporting hybrid data scenarios
  • IoT integration for manufacturing and supply chain automation

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.

APIsWeb ServicesBusiness CentralMicrosoft Dynamics 365IntegrationAutomationBusiness Solutions
Choosing the right ERP consulting partner can make all the difference. At BusinessCentralNav, we combine deep industry insight with hands-on Microsoft Business Central expertise to help you simplify operations, improve visibility, and drive growth. Our approach is rooted in collaboration, transparency, and a genuine commitment to delivering real business value—every step of the way.

Let`'s talk

Explore Business Central Posts

image

Boost Efficiency with Power Automate and Business Central

Learn how integrating Microsoft Power Automate with Dynamics 365 Business Central can transform your business operations, automate workflows, and enhance productivity.

By

Matias Orlando

Date

2023-12-15

image

Transitioning from C/AL to RoleTailored in Dynamics NAV

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.

By

Kery Nguyen

Date

2023-12-15

image

Developing Extensions in Business Central Using VS Code

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.

By

Kery Nguyen

Date

2023-12-15