Implementing Business Events in D365FO
Business Events in Dynamics 365 Finance and Operations (D365FO) provide a powerful, event-driven integration mechanism that allows external systems to react to business processes in near real time. They enable D365FO to publish meaningful business signals, such as order confirmations or approvals.
In this article, we’ll explore:
What Business Events are and when to use them
How are they triggered in D365FO
How to implement a custom Business Event using X++
How to consume Business Events using Azure Logic Apps
Real-world integration considerations and best practices
This guide is written for D365FO developers, architects, and technical decision-makers looking to build scalable, modern integrations.
What Are Business Events in D365FO?
Business Events allow D365FO to notify external systems when specific business processes or user actions occur. These events can then be consumed by services such as:
Microsoft Power Automate
Azure Logic Apps
Azure Service Bus
Azure Event Grid
This enables event-driven integrations, reducing the need for polling, batch jobs, or custom APIs.
Examples of Business Event Triggers :
Business events can be raised from:
Workflow-based actions
Approving a purchase requisition
Approving a vendor invoice
Non-workflow business processes
Confirming a sales order
Posting a packing slip
Posting an invoice
Both types can generate Business Events that downstream systems can react to immediately.
Important: To consume Business Events, customers must have valid subscriptions to Azure services or Power Platform components such as Logic Apps or Power Automate.
Out-of-the-Box vs Custom Business Events
D365FO provides several Business Events out of the box, especially around workflows and core processes. However, real-world projects often require:
Publishing events for custom processes
Extending existing processes with additional payload data
Aligning events with specific integration contracts
In these cases, custom Business Events must be implemented using X++.
How Business Events Are Implemented in D365FO
At a high level, implementing a Business Event involves three steps:
Build the Business Event contract
Build the Business Event class
Trigger the event from a business process
Technically, this requires implementing two core classes and extending the relevant business process.
Step 1: Business Event Class (Event Definition)
The Business Event class:
Extends
BusinessEventsBaseDefines metadata (name, description, module)
Builds and sends the payload
Example: Packing Slip Posted Business Event
[BusinessEvents(classStr(FTDPackingSlipBusinessEventContract),
'Packing Slip Posted BE',
'Triggered when packing slip is posted',
ModuleAxapta::SalesOrder)]
public final class FTDPackSlipPostedBusinessEvent extends BusinessEventsBase
{
private SalesTable salesTable;
private SalesTable parmSalesTable(SalesTable _salesTable = salesTable)
{
salesTable = _salesTable;
return salesTable;
}
public static FTDPackSlipPostedBusinessEvent newFromSalesTable(SalesTable _salesTable)
{
FTDPackSlipPostedBusinessEvent businessEvent = new FTDPackSlipPostedBusinessEvent();
businessEvent.parmSalesTable(_salesTable);
return businessEvent;
}
private void new()
{
}
[Wrappable(true), Replaceable(true)]
public BusinessEventsContract buildContract()
{
return FTDPackingSlipBusinessEventContract::newFromSalesTable(salesTable);
}
}
Key Architectural Notes
BusinessEventsBasehandles publishing and lifecycle managementThe
[BusinessEvents]attribute registers the event in D365FObuildContract()defines the payload structure
Step 2: Business Event Contract (Payload Definition)
The contract:
Extends
BusinessEventsContractDefines the event payload
Is serialized and sent to external systems
Example: Packing Slip Business Event Contract
[DataContract]
public final class FTDPackingSlipBusinessEventContract extends BusinessEventsContract
{
private SalesIdBase salesId;
private LegalEntityDataAreaId legalEntity;
private String50 salesStatus;
public static FTDPackingSlipBusinessEventContract newFromSalesTable(SalesTable _salesTable)
{
var contract = new FTDPackingSlipBusinessEventContract();
contract.initialize(_salesTable);
return contract;
}
private void new()
{
}
private void initialize(SalesTable _salesTable)
{
salesId = _salesTable.SalesId;
salesStatus = enum2Symbol(enumNum(SalesStatus), _salesTable.SalesStatus);
}
[DataMember('SalesOrderId'), BusinessEventsDataMember("@AccountsReceivable:SalesOrderId")]
public SalesIdBase parmSaleOrderId(SalesIdBase _salesId = salesId)
{
salesId = _salesId;
return salesId;
}
[DataMember('SalesStatus'), BusinessEventsDataMember("@AccountsReceivable:SalesStatus")]
public String50 parmSalesStatus(String50 _salesStatus = salesStatus)
{
salesStatus = _salesStatus;
return salesStatus;
}
[DataMember('LegalEntity'), BusinessEventsDataMember("@AccountsReceivable:LegalEntity")]
public LegalEntityDataAreaId parmLegalEntity(LegalEntityDataAreaId _legalEntity = legalEntity)
{
legalEntity = _legalEntity;
return legalEntity;
}
} Best Practices for Contracts
Keep payloads small and meaningful
Avoid sensitive data unless strictly required
Use stable identifiers (e.g., SalesId, RecId)
Treat contracts as public APIs
Step 3: Trigger the Business Event from the Business Process
The final step is extending the relevant business logic and sending the event at the correct point in the process.
Example: Extend Packing Slip Posting
[ExtensionOf(classStr(SalesPackingSlipJournalPost))]
public final class FTDSalesPackingSlipJournalPost_Extension
{
protected void endPost()
{
next endPost();
if (chainFormLetterContract.parmVersioningUpdateType() == VersioningUpdateType::Initial)
{
FTDPackSlipPostedBusinessEvent::newFromSalesTable(salesTable).send();
}
}
} Why This Matters
Ensures the event is sent only after successful posting
Prevents duplicate or invalid messages
Aligns with D365FO extensibility best practices
Consuming Business Events Using Azure Logic Apps
One of the most common and powerful patterns is consuming D365FO Business Events in Azure Logic Apps.
High-Level Integration Flow
D365FO publishes a Business Event
Logic App is triggered
Payload is parsed to JSON
Message is forwarded to Azure services (Queue, Service Bus, Event Grid)
Step-by-Step: Logic App Configuration
1. Create a Logic App
Go to Azure Portal → Logic Apps → Create
Select subscription, resource group, and region
Choose Consumption or Standard based on scale needs
2. Add the Business Event Trigger
Open Logic App Designer
Search for “When a business event occurs”
Configure:
D365FO environment
Legal entity
Business Event name (your custom event)
3. Parse JSON Payload
Add Parse JSON action
Use the trigger body as content
Generate schema using a sample payload from D365FO
4. Store the Message in Azure Queue
Add Put a message on a queue
Select:
Azure Storage Account
Target Queue
Use parsed JSON as message content
This pattern is ideal for decoupled, scalable integrations.
Testing and Troubleshooting
To test the integration:
Execute the business process in D365FO (e.g., post packing slip)
Check Logic App Run History
Validate payload structure and queue message
Monitor failures and retries in Azure
Final Thoughts: Why Business Events Matter
Business Events are a strategic integration feature in D365FO:
Promote event-driven architecture
Reduce tight coupling
Improve responsiveness
Align perfectly with Azure-native services
For customers and partners building modern D365FO solutions, Business Events are no longer optional; they are a best practice.
