Connect Microsoft Dynamics 365 Business Central to AI Agents: Supply
A technical guide to connecting Microsoft Dynamics 365 Business Central to AI agents for supply chain automation, purchase order generation, and vendor management.
You want to connect Microsoft Dynamics 365 Business Central to an AI agent so your system can independently manage vendors, sync purchase orders, track inventory items, and analyze accounts payable. Here is exactly how to do it using Truto's /tools endpoint and SDK, bypassing the need to hand-code complex OData integrations manually.
Giving a Large Language Model (LLM) read and write access to a monolithic ERP like Dynamics 365 Business Central is a significant engineering challenge. You either spend months building, hosting, and maintaining a custom connector that navigates Microsoft's strict data hierarchies, or you use a managed infrastructure layer that handles the boilerplate for you. If your team uses ChatGPT, check out our guide on connecting Microsoft Dynamics 365 Business Central to ChatGPT, or if you are building on Anthropic's models, read our guide on connecting Microsoft Dynamics 365 Business Central to Claude. For developers building custom autonomous workflows, you need a programmatic way to fetch these tools and bind them to your agent framework.
This guide breaks down exactly how to fetch AI-ready tools for the supply chain side of Business Central, bind them natively to an LLM using your preferred framework (LangChain, LangGraph, Vercel AI SDK, etc.), and execute complex procurement workflows.
Why a Unified Tool Layer Matters for Agent Safety
Before writing a line of integration code, decide what layer your agent talks to. This choice dictates the reliability of your production system.
Direct API tools - mapping one tool directly to the raw Business Central endpoint - push vendor-specific architectural quirks directly into the LLM's context window. The model has to "remember" the exact format of an OData query, the specific data types expected for currency fields, and the strict ETag concurrency rules for updates. Every one of those quirks is a hallucination waiting to happen.
A unified tool layer abstracts these quirks behind stable, predictable JSON schemas. The agent sees a deterministic tool called create_a_microsoft_dynamics_365_business_central_purchase_order, not a complex /purchaseOrders REST endpoint requiring a deep understanding of Microsoft's navigation properties. This provides concrete engineering advantages:
- Smaller attack surface for hallucination. The LLM only chooses from defined function names. It does not invent URL paths or guess required query parameters.
- Deterministic input validation. Every tool has a strict JSON schema. Invalid arguments are caught and rejected locally before they even hit the ERP, failing fast and allowing the agent to self-correct.
- Decoupled authentication. The agent runtime never handles raw API keys or OAuth access tokens. It only holds a single bearer token for the unified API, removing a massive security risk from the execution loop.
For a deeper look at the architectural theory behind this approach, refer to our research on architecting AI agents and the SaaS integration bottleneck.
The Engineering Reality of Custom Business Central Connectors
Building AI agents is the easy part. Connecting them to external SaaS APIs safely is where systems fail. If you decide to hand-roll your integration to Business Central, you own the entire API lifecycle. Business Central introduces several highly specific integration challenges that consistently break standard LLM assumptions.
The OData Navigation Trap
Business Central APIs rely on OData V4 standards. While OData provides powerful querying capabilities, it is hostile to LLMs. Standard REST conventions do not apply. If an agent needs to retrieve a vendor and all their associated purchase history, it has to format a URL string perfectly using $expand, $filter, and $select operators.
If you hand-code this, you must write extensive system prompts teaching the LLM the exact syntax of OData filtering. When the LLM inevitably hallucinates a filter property (e.g., using eq instead of = or wrapping an integer in quotes), the API rejects the request. Truto maps these endpoints to standard Proxy APIs, converting the raw BC endpoints into a flat, predictable REST-based CRUD interface where the agent just passes query parameters described by the tool schema.
Strict Concurrency with ETags
Business Central enforces strict optimistic concurrency. You cannot blindly update or delete a record. To modify a vendor, the system must first perform a GET request to retrieve the current @odata.etag value, and then pass that exact value in an If-Match header during the PATCH or DELETE request.
Agents are notoriously bad at remembering to pass obscure header values across multi-step sequences. By using Truto's tool layer, the etag is exposed as a strongly typed, required property in the update tool's schema. If the agent tries to update a record without it, the tool schema validation rejects the call immediately, prompting the agent to fetch the ETag first.
Company-Scoped Resources
A single Business Central tenant often contains multiple operating companies. Every single API request requires the context of a specific company_id. If an agent attempts to retrieve purchase orders globally, the request fails. The tool layer forces the agent to acknowledge this architecture by requiring company_id on virtually every resource call, preventing scoping errors at the prompt level.
Fetching Tools from the Proxy API
Every integration on Truto represents how the underlying product's API behaves, defined by a comprehensive schema. Truto abstracts these endpoints into Resources (e.g., vendors, purchase_orders) and Methods (List, Get, Create, Update).
To make these accessible to AI frameworks, you simply call the Truto /integrated-account/<id>/tools endpoint. This returns all the available Proxy APIs alongside their descriptions and strict JSON schemas, fully formatted for LLM consumption. Your framework (like LangChain) ingests these descriptions and automatically handles the tool binding.
Hero Tools for Supply Chain Automation
Rather than dumping dozens of endpoints into your agent's context, you provide high-leverage tools that map to specific supply chain operations. Here are the core tools your agent needs to manage procurement and vendors in Business Central.
List All Vendors
Retrieves the directory of suppliers in the specified Business Central company. This is usually the first step in a procurement workflow, allowing the agent to resolve a vendor name to a specific vendorId or vendorNumber.
Contextual usage notes: The agent must provide the environment and company_id. The return payload includes address data, tax registration, payment terms, and the current outstanding balance, which is highly useful for automated risk assessments.
"Find the vendor ID and current account balance for 'Contoso Electronics' in the US operating company."
Create a Purchase Order
Generates a new draft purchase order. This tool initiates the procurement cycle.
Contextual usage notes: The LLM must specify the company_id and either vendorId or vendorNumber. The response includes the generated PO number and defaults the status to Draft. The agent can subsequently use the purchase order lines tools to populate the document.
"Create a new draft purchase order for vendor ID 445A-88B2 in the EU entity. Note the new PO number so we can add items to it."
List All Purchase Invoices
Fetches accounts payable invoices, automatically expanding the invoice line items and dimension sets.
Contextual usage notes: Because Truto handles the OData $expand logic behind the scenes, this tool returns a deeply nested, complete view of the invoice without requiring the agent to execute secondary lookups for the line items. This is critical for automated invoice reconciliation.
"Retrieve all open purchase invoices from last week for our logistics vendors, and sum up the total tax amounts."
List All Purchase Order Lines
Retrieves the specific line items associated with a given purchase order.
Contextual usage notes: The agent must provide the purchase_order_id. This tool is used heavily in receiving workflows, allowing the agent to verify if the receivedQuantity matches the ordered quantity.
"Get the line items for purchase order PO-9932 and check if any items are still pending receipt."
List All Vendor Purchases
Provides a summary of total purchase amounts aggregated by vendor.
Contextual usage notes: This tool is ideal for reporting and analytical agents. Instead of forcing the agent to pull thousands of individual invoices and sum them up (which would blow out the context window), this endpoint returns the pre-calculated totalPurchaseAmount per vendor.
"Generate a report showing our top 5 suppliers by total purchase volume for the current fiscal year."
List All Aged Accounts Payables
Returns the aging buckets for outstanding vendor balances.
Contextual usage notes: Essential for cash flow management workflows. The agent receives the balanceDue broken down into period buckets (e.g., 30 days, 60 days, 90+ days), allowing it to prioritize payments or flag overdue accounts automatically.
"Review our accounts payable and identify any vendors with balances sitting in the 60+ days overdue bucket."
List All Items
Retrieves the master catalog of inventory items, non-inventory items, and services.
Contextual usage notes: Before an agent can add a line to a purchase order, it must know the exact itemId or number. This tool returns the item catalog along with current inventory levels and unitCost.
"Check the current inventory levels for 'Steel Bearings' (Item 1004). If the count is below 50, alert the procurement team."
For the complete inventory of available supply chain and financial tools, and their exact schema requirements, refer to the Microsoft Dynamics 365 Business Central integration page.
Workflows in Action
By chaining these tools together, your AI agent can execute complex, multi-step operations that traditionally required manual data entry by procurement staff.
Scenario 1: Automated Inventory Restock
"Check the inventory level for Item 4001 (Copper Wire). If we have fewer than 100 units in stock, find our primary supplier for wire and create a draft purchase order for 200 units."
- list_all_microsoft_dynamics_365_business_central_items: The agent queries the item catalog, filtering for Item 4001, and reads the
inventoryfield. Seeing the stock is at 45, it proceeds. - list_all_microsoft_dynamics_365_business_central_vendors: The agent searches the vendor list for the designated supplier to retrieve their
vendorId. - create_a_microsoft_dynamics_365_business_central_purchase_order: The agent creates the header for the new PO using the retrieved
vendorId. - create_purchase_order_line: (A tool available in the full inventory) The agent adds a line item to the newly created PO for 200 units of Item 4001.
Outcome: The system independently identifies a low-stock scenario and generates a complete, draft purchase order ready for a human manager's final review and approval.
Scenario 2: Accounts Payable Prioritization
"Run an analysis on our aged payables. Identify any vendors with balances over 60 days late, and cross-reference that with our total purchase volume from them to see if they are a strategic supplier."
- list_all_aged_accounts_payables: The agent pulls the aging report, identifying three vendors with balances in the
period3Amountbucket (60+ days). - list_all_vendor_purchases: The agent queries the total purchase history for those three specific vendors.
- Reasoning step: The LLM compares the overdue amounts against the total historical volume, identifying that one vendor accounts for 40% of the company's total spend.
Outcome: The agent outputs a prioritized list of critical payments, flagging the strategic supplier for immediate settlement to avoid supply chain disruptions.
Building Multi-Step Workflows
To run these workflows in production, your agent must be able to handle network realities. The most critical aspect of building against third-party SaaS APIs is handling rate limits.
The Reality of Rate Limits
Business Central enforces rate limits to protect its infrastructure. Factual note on rate limits: Truto does not automatically retry, throttle, or apply backoff when an upstream API hits a rate limit. If Business Central returns an HTTP 429 (Too Many Requests), Truto passes that error directly to your application.
Truto normalizes the upstream rate limit information into standardized IETF headers: ratelimit-limit, ratelimit-remaining, and ratelimit-reset. It is the responsibility of the caller (your agent execution loop) to intercept the 429 status, read the ratelimit-reset header, and execute a sleep/backoff before retrying the tool call. Do not assume the integration layer will magically absorb the delay.
Implementing the Execution Loop
Here is how you structure a framework-agnostic execution loop (using TypeScript) that fetches the tools, invokes the LLM, and strictly handles 429 rate limit responses.
import { TrutoToolManager } from 'truto-langchainjs-toolset';
import { ChatOpenAI } from '@langchain/openai';
async function runSupplyChainAgent(prompt: string, integratedAccountId: string) {
// 1. Initialize the manager and fetch BC tools via Truto API
const toolManager = new TrutoToolManager({
trutoToken: process.env.TRUTO_API_KEY,
integratedAccountId: integratedAccountId,
});
const tools = await toolManager.getTools();
// 2. Bind the normalized tools to the LLM
const llm = new ChatOpenAI({ modelName: 'gpt-4o' }).bindTools(tools);
let messages = [{ role: 'user', content: prompt }];
let executionComplete = false;
// 3. Establish the execution loop
while (!executionComplete) {
const response = await llm.invoke(messages);
messages.push(response);
if (!response.tool_calls || response.tool_calls.length === 0) {
console.log("Agent finished:", response.content);
executionComplete = true;
continue;
}
// 4. Execute requested tools
for (const toolCall of response.tool_calls) {
let toolSuccess = false;
let retryCount = 0;
const maxRetries = 3;
while (!toolSuccess && retryCount < maxRetries) {
try {
console.log(`Executing tool: ${toolCall.name}`);
const tool = tools.find(t => t.name === toolCall.name);
const result = await tool.invoke(toolCall.args);
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: JSON.stringify(result)
});
toolSuccess = true;
} catch (error: any) {
// 5. Explicitly handle the 429 Rate Limit passed through from Truto
if (error.status === 429) {
const resetHeader = error.headers?.['ratelimit-reset'];
// Default to 5 seconds if header is missing
const sleepSeconds = resetHeader ? parseInt(resetHeader, 10) : 5;
console.warn(`Rate limit hit on ${toolCall.name}. Sleeping for ${sleepSeconds}s...`);
await new Promise(resolve => setTimeout(resolve, sleepSeconds * 1000));
retryCount++;
} else {
// Pass non-retryable errors back to the LLM to handle gracefully
messages.push({
role: 'tool',
tool_call_id: toolCall.id,
content: `Error executing tool: ${error.message}`
});
break; // Break the retry loop on hard errors (like 400 Bad Request)
}
}
}
}
}
}The Architecture Behind the Loop
The interaction between your agent, the tool layer, and Business Central flows sequentially, ensuring that compliance and rate limiting are handled deterministically at the boundaries.
sequenceDiagram participant Agent as AI Agent Loop participant ToolLayer as Truto Tool Manager participant Upstream as Upstream API (BC) Agent->>ToolLayer: Invoke "create_a_microsoft_dynamics_365_business_central_purchase_order" ToolLayer->>Upstream: POST /purchaseOrders Upstream-->>ToolLayer: HTTP 429 Too Many Requests ToolLayer-->>Agent: HTTP 429 (ratelimit-reset: 10) Note over Agent: Agent executes 10s sleep Agent->>ToolLayer: Retry "create_a_microsoft_dynamics_365_business_central_purchase_order" ToolLayer->>Upstream: POST /purchaseOrders Upstream-->>ToolLayer: HTTP 201 Created ToolLayer-->>Agent: Purchase Order JSON
By putting the retry logic in the agent loop rather than masking it inside the integration layer, your application retains complete control over execution context. If a rate limit reset is 15 minutes, your system can suspend the agent's run state to a database and resume it later, rather than holding an HTTP connection open and blocking a server thread.
Moving from Manual to Autonomous ERP
Enterprise resource planning systems were designed for humans clicking through complex graphical interfaces. AI agents require flat, deterministic, explicitly schematized function calls.
Attempting to bridge that gap by handwriting OData translation layers and maintaining token lifecycles for Business Central is a massive distraction from building your core AI product. By utilizing an automated tool generation endpoint, you give your agents robust, type-safe access to supply chain data in minutes, not months.
FAQ
- Does Truto automatically handle Microsoft Dynamics 365 Business Central rate limits?
- No. Truto does not retry, throttle, or apply backoff on rate limit errors. When Business Central returns an HTTP 429, Truto passes the error directly to the caller and normalizes the upstream info into standardized IETF headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset). The caller's execution loop is responsible for implementing retry and backoff logic.
- How do AI agents handle Business Central's strict ETag concurrency requirements?
- For update and delete operations, Business Central requires a valid ETag. Your agent workflow should be designed to execute a read tool first (e.g., get_purchase_order_by_id), extract the returned ETag, and pass it into the subsequent write tool (e.g., update_purchase_order_by_id). The unified tool schema enforces this dependency.
- Can I use any agent framework with Truto's Business Central tools?
- Yes. Truto's /tools endpoint returns standard JSON schemas describing every available Proxy API method. These can be easily bound to any framework, including LangChain, LangGraph, CrewAI, or the Vercel AI SDK, using standard function calling paradigms.
- Do I need to teach the LLM how to write OData queries?
- No. The unified tool layer abstracts the OData V4 complexities. Instead of teaching the model how to construct $filter or $expand operators, the LLM simply calls heavily typed tools like list_all_microsoft_dynamics_365_business_central_vendors with standard JSON arguments.