Back to blog
Unified APIGuides & TutorialsAccounting

Building Expense Integrations for Microsoft Business Central API

Master Microsoft Business Central expense API integration with practical examples, authentication setup, and purchase invoice management. Learn how unified APIs can reduce weeks of complex integration work to days.

Saurabh RaiSaurabh Rai

Saurabh Rai

9 min read
Building Expense Integrations for Microsoft Business Central API

If you're trying to manage expenses with the Microsoft Business Central API, you've probably realized it's surprisingly complex. Knowing whether to use a purchase invoice, a vendor bill, or an expense report endpoint is a common struggle for developers.

This guide focuses on the practical implementation of expense management through Business Central's REST APIs. We'll work with real examples, show you the exact JSON payloads you need, and then demonstrate how a unified API approach can eliminate most of the complexity.

Understanding Business Central's API Architecture

Business Central exposes its functionality through RESTful APIs using the OData protocol. Every API call follows a consistent URL pattern that developers need to understand before diving into expense management.

The base URL structure looks like this:

https://api.businesscentral.dynamics.com/v2.0/{tenant-id}/{environment}/api/v2.0/

For on-premises installations, the pattern changes slightly:

https://{server}:{port}/{instance}/api/v2.0/

Business Central comes with built-in APIs that require no configuration. These APIs handle standard business objects like customers, vendors, items, and, importantly for us, purchase invoices and expense documents. The API stack is optimized for performance and follows OData v4 conventions, allowing you to use standard query parameters like $filter, $select, and $expand.

Expense Management in Business Central

In Business Central, expenses typically flow through purchase invoices. When an employee submits an expense report or when your company receives a vendor bill, these transactions create purchase invoice records.

A purchase invoice in Business Central consists of two main components: the header containing vendor information and metadata, and the lines containing individual expense items. This structure allows for complex expense scenarios like split allocations, multiple tax rates, and departmental chargebacks.

Authentication and Setup

Before making any API calls, you need proper authentication. Business Central supports several authentication methods, but OAuth 2.0 is recommended for production environments.

First, register your application in Azure Active Directory:

const tokenEndpoint = 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token';

const tokenRequest = {
  client_id: 'your-client-id',
  client_secret: 'your-client-secret',
  scope: 'https://api.businesscentral.dynamics.com/.default',
  grant_type: 'client_credentials'
};

// Get access token
const tokenResponse = await fetch(tokenEndpoint, {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams(tokenRequest)
});

const { access_token } = await tokenResponse.json();

For development and testing, you can use basic authentication with web service access keys generated from within Business Central. Navigate to Users, select your integration user, and generate a web service access key.

Creating Expense Records: Purchase Invoice API

The purchase invoice API is your primary tool for creating expense records. Here's a complete example of creating an expense submission:

const baseUrl = 'https://api.businesscentral.dynamics.com/v2.0/tenant-id/production/api/v2.0';
const companyId = 'your-company-id';

// Per Business Central's data model, we must first create the invoice header...
const purchaseInvoice = {
  "vendorNumber": "V00100",
  "invoiceDate": "2025-01-15",
  "postingDate": "2025-01-15",
  "dueDate": "2025-02-15",
  "vendorInvoiceNumber": "EXP-2025-001",
  "currencyCode": "USD",
  "paymentTermsId": "00000000-0000-0000-0000-000000000001"
};

const invoiceResponse = await fetch(`${baseUrl}/companies(${companyId})/purchaseInvoices`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${access_token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(purchaseInvoice)
});

const invoice = await invoiceResponse.json();

After creating the header, add expense line items:

// ...and then attach line items to its unique ID
const expenseLine = {
  "documentId": invoice.id,
  "sequence": 10000,
  "lineType": "G/L Account",
  "lineObjectNumber": "6420",  // Travel expenses account
  "description": "Flight to client meeting - NYC",
  "quantity": 1,
  "unitCost": 450.00,
  "amountExcludingTax": 450.00,
  "taxCode": "STANDARD"
};

await fetch(`${baseUrl}/companies(${companyId})/purchaseInvoices(${invoice.id})/purchaseInvoiceLines`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${access_token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(expenseLine)
});

Retrieving and Updating Expenses

To retrieve existing expense records, use GET requests with OData query parameters:

// Get all unposted purchase invoices for expense review
const response = await fetch(
  `${baseUrl}/companies(${companyId})/purchaseInvoices?$filter=status eq 'Draft'&$expand=purchaseInvoiceLines`,
  {
    headers: { 'Authorization': `Bearer ${access_token}` }
  }
);

const expenses = await response.json();

// Update an expense amount after manager review
const updateData = {
  "unitCost": 425.00,
  "amountExcludingTax": 425.00
};

await fetch(
  `${baseUrl}/companies(${companyId})/purchaseInvoiceLines(${lineId})`,
  {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${access_token}`,
      'Content-Type': 'application/json',
      'If-Match': '*'
    },
    body: JSON.stringify(updateData)
  }
);

Note the 'If-Match': '*' header, which Business Central requires to prevent accidental overwrites when updating records.

Handling Attachments

Expense reports often require receipt attachments. Business Central's API supports document attachments through a separate endpoint:

// Upload receipt attachment
const attachmentData = {
  "parentType": "Purchase Invoice",
  "parentId": invoice.id,
  "fileName": "receipt_2025_001.pdf",
  "attachmentContent": base64EncodedContent
};

await fetch(`${baseUrl}/companies(${companyId})/attachments`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${access_token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(attachmentData)
});

Common Integration Challenges

Directly integrating with Business Central's API is challenging for several key reasons. You have to learn Business Central's entire data model, including the relationships between vendors, G/L accounts, dimensions, and tax codes. Field names don't always match business terminology, and error messages can be cryptic.

Rate limiting is another concern. Business Central enforces throttling to prevent service degradation. Production environments typically allow 100 requests per user per minute, with burst capacity up to 200 requests. Exceeding these limits results in 429 responses that require retry logic.

The complexity multiplies when supporting multiple accounting systems. Each platform has its own authentication method, data structure, and business rules. What Business Central calls a "purchaseInvoice" might be a "bill" in QuickBooks or a "supplier invoice" in Xero.

Simplifying with Unified APIs: The Apideck Approach

Instead of building and maintaining separate integrations for each accounting platform, Apideck provides a unified API that abstracts away platform-specific complexity. This approach transforms weeks of integration work into days.

With Apideck's Accounting API, the same expense creation looks like this:

import { Apideck } from '@apideck/unify';

// Initialize the Apideck client
const apideck = new Apideck({
  apiKey: 'your-api-key',      // Your Apideck API key
  consumerId: 'customer-id',   // Unique ID for the end-user or customer
  appId: 'your-app-id'         // Your Apideck application ID
});

// Create a simple expense
async function createExpense() {
  try {
    const response = await apideck.accounting.expenses.create({
      serviceId: 'microsoft-dynamics-365-business-central',  // Target service
      account: { id: 'travel-expenses' },                    // Ledger account ID
      transaction_date: '2025-01-15',                        // ISO date format
      line_items: [{
        description: 'Flight to client meeting - NYC',
        total_amount: 450.00,                                // Line item amount
        tax_rate: { id: 'standard-rate' }                    // Tax rate ID
      }],
      currency: 'USD'                                        // ISO currency code
    });

    console.log('Expense created successfully:', response);
  } catch (error) {
    console.error('Error creating expense:', error);
  }
}

createExpense();

The same code works across Business Central, QuickBooks, Xero, NetSuite, and 20+ other accounting platforms. Apideck handles the translation between your unified data model and each platform's specific requirements.

Real-World Implementation Benefits

Consider a typical expense management application that needs to support multiple accounting systems. Without a unified API, you would need to:

  • Learn each platform's API documentation
  • Handle different authentication flows
  • Map data fields for each system
  • Maintain separate error handling logic
  • Keep up with API version changes

Apideck eliminates this complexity by providing:

One API to Learn, Not Dozens: Single integration point with one SDK to implement and one set of documentation to reference.

Stop Worrying About Field Names: Apideck translates between your data model and each platform's requirements. Send "expense" and it becomes "purchaseInvoice" for Business Central, "bill" for QuickBooks, or "supplierInvoice" for Xero.

Consistent Errors You Can Actually Debug: Clear error messages that make sense across all platforms, instead of cryptic platform-specific codes.

Automatic Retries for Rate Limits: Built-in handling of throttling with exponential backoff, so you don't have to write retry logic for each platform.

business central api integration

For Business Central specifically, Apideck handles the complexity of purchase invoice creation, line item management, tax calculations, and attachment uploads through a single, consistent interface.

Conclusion

Microsoft Business Central's expense APIs are powerful but require significant investment to implement correctly. Between authentication setup, understanding the data model, and handling edge cases, a direct integration can take weeks to build and months to stabilize.

A unified API approach through platforms like Apideck cuts this complexity dramatically. Instead of maintaining separate integrations for each accounting system your customers use, you build once and deploy everywhere. This saves you from integration maintenance hell and lets you focus on your actual product.

Whether you integrate directly or use a unified API, remember that expense management is about more than just moving data. Handle edge cases gracefully, provide clear feedback when things break, and make sure your solution scales as transaction volumes grow.

For teams developing expense management, accounts payable, or financial automation tools, the decision between direct and unified APIs often hinges on resources and scope. If you're only supporting Business Central, a direct integration might work. But if you need to support multiple platforms or want to move fast, a unified API gives you the abstraction layer that lets you ship features instead of debugging integrations.

Ready to get started?

Scale your integration strategy and deliver the integrations your customers need in record time.

Ready to get started?
Talk to an expert

Trusted by fast-moving product & engineering teams

Nmbrs
Benefex
Invoice2go by BILL
Trengo
Ponto | Isabel Group
Apideck Blog

Insights, guides, and updates from Apideck

Discover company news, API insights, and expert blog posts. Explore practical integration guides and tech articles to make the most of Apideck's platform.

REST vs. SOAP APIs
Guides & Tutorials

REST vs. SOAP APIs

Learn the real differences between REST, SOAP, and GraphQL APIs with practical code examples, performance metrics, and when to actually use each one.

Saurabh Rai

Saurabh Rai

8 min read
QuickBooks API Pricing and the Intuit App Partner Program
AccountingIndustry insights

QuickBooks API Pricing and the Intuit App Partner Program

If you're planning to build an integration with the QuickBooks Online API, understanding the pricing structure is crucial for budgeting and architectural decisions. While the QuickBooks API was historically free with no volume restrictions, Intuit introduced a significant change in 2025 with the launch of the Intuit App Partner Program.

GJ

GJ

5 min read
Complete Guide to SAP S/4HANA APIs: REST and SOAP Integration Tutorial
Unified APIGuides & Tutorials

Complete Guide to SAP S/4HANA APIs: REST and SOAP Integration Tutorial

Master SAP S/4HANA integration with this complete guide to REST and SOAP APIs. Learn authentication methods, code examples, rate limiting strategies, and when to use OData vs SOAP for enterprise system connectivity.

Saurabh Rai

Saurabh Rai

8 min read