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

JobNimbus
Blue Zinc
Drata
Octa
Nmbrs
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.

What is API Integration
Guides & Tutorials

What is API Integration

API integration is the process of connecting two or more software systems through their APIs so they can exchange data.

Saurabh Rai

Saurabh Rai

12 min read
Untangling The Sage Ecosystem
AccountingIndustry insights

Untangling The Sage Ecosystem

Sage isn't one product; it’s a federation of disconnected legacy and cloud portfolios built through decades of acquisitions. We decode the "Sage Paradox" to help you distinguish the brand from the technology.

Bernard Willems

Bernard Willems

4 min read
How to Get Your BambooHR API Keys
Blog

How to Get Your BambooHR API Keys

BambooHR's API authentication is deceptively simple - just an API key. But finding where to generate it and understanding the permission model trips up most developers. This guide cuts through the confusion.

Saurabh Rai

Saurabh Rai

8 min read