In Dynamics 365 Finance & Operations (D365 FO), data integration is a critical requirement for organizations that rely on external systems such as Master Data Management (MDM) tools. A common scenario is when you want to create or update product master data in D365 FO automatically instead of manual entry or file upload.
This is where Custom Data Entity OData Actions come in.
A data entity in D365 FO is an abstraction that represents a business object (for example, Customer, Vendor, Item).
By default, data entities support CRUD operations via OData (Create, Read, Update, Delete).
But sometimes, we need to perform custom business logic during integration that goes beyond standard CRUD.
👉 That’s where a custom OData Action comes in:
It is a method (defined in X++) that can be exposed as an API endpoint.
You can pass multiple parameters, execute your own logic, and return a result.
Think of it like:
🛠️ “An API hook into your business logic inside D365 FO, built on top of data entities.”
Here’s a simplified view of a custom OData action class:
public class ANSARIPilogFinishedGoodsIntSingleDataEntity extends common { [SysODataAction("processPilog", false), SysODataCollectionAttribute("return", Types::String)] public static str processPilog( ItemId ITEM, Name PRODUCTNAME, InventSiteId SITEID, InventLocationId WAREHOUSEID, EcoResProductDisplayProductNumber PRODUCTNUMBER, ItemGroupId PRODUCTGROUPID, UnitOfMeasureSymbol PURCHASEUNITSYMBOL, UnitOfMeasureSymbol SALESUNITSYMBOL, ItemNetWeight NETPRODUCTWEIGHT, ItemVolume PRODUCTVOLUME, LanguageIdAll LANGUAGE, DataAreaId DATAAREAID ) { str ret; changecompany(DATAAREAID) { ANSARIPilogFinishedGoodsManager manager = ANSARIPilogFinishedGoodsManager::construct(); ret = manager.process( ITEM, PRODUCTNAME, SITEID, WAREHOUSEID, PRODUCTNUMBER, PRODUCTGROUPID, PURCHASEUNITSYMBOL, SALESUNITSYMBOL, NETPRODUCTWEIGHT, PRODUCTVOLUME, LANGUAGE ); } return ret; } }
[SysODataAction("processPilog", false), SysODataCollectionAttribute("return", Types::String)]
This is called an attribute declaration. Attributes in X++ are metadata that describe how the class or method should behave, especially in the context of OData services.
SysODataAction("processPilog", false)This attribute is used to expose a method as a custom OData action.
"processPilog" → This is the name of the OData action that external clients (like Power Automate, Power BI, or Postman) can call.
false → Indicates whether this action is bound or unbound:
true → The action is bound to a specific entity (you call it on a specific record).
false → The action is unbound, meaning it can be called independently, not tied to a particular record.
In simple terms:
This method can be invoked via OData using the name processPilog, and it is unbound, so it can run without referencing a specific record.
SysODataCollectionAttribute("return", Types::String)This attribute specifies what the method returns for OData clients.
"return" → Indicates the output property of the OData action.
Types::String → The data type of the return value, in this case, a string.
Essentially, this tells OData that this action returns a collection of strings (or a string type in the response).
External System (PiLog or any MDM tool) sends product details using API call.
The API hits the OData action processPilog.
The method switches to the correct company (legal entity) using changecompany(DATAAREAID).
Calls a manager class (ANSARIPilogFinishedGoodsManager) that:
Validates data.
Creates or updates product in EcoResProduct, InventTable, etc.
Applies business rules.
Returns a status message like “SUCCESS: Item created” or “ERROR: Missing dimension group”.
Standard DMF entities only handle raw insert/update.
They don’t apply all your company-specific business rules.
For example:
Validate whether warehouse and site combination is valid.
Ensure a specific product group is assigned.
Apply default planning parameters.
👉 That’s why a custom OData action is required — it ensures real-time integration while keeping business rules intact.
Request (JSON):
{ "ITEM": "FG-1001", "PRODUCTNAME": "Ansari Tea Pack", "SITEID": "USMF", "WAREHOUSEID": "MAIN", "PRODUCTNUMBER": "TEA001", "PRODUCTGROUPID": "TEA", "PURCHASEUNITSYMBOL": "EA", "SALESUNITSYMBOL": "EA", "NETPRODUCTWEIGHT": 1.2, "PRODUCTVOLUME": 0.5, "LANGUAGE": "en-us", "DATAAREAID": "USMF" }
Response:
{ "value": "SUCCESS: Item FG-1001 created in USMF" }
Let’s go step by step to call your processPilog OData action from Postman. I’ll explain it clearly so you can try it directly.
Since your class is:
public class ANSARIPilogFinishedGoodsIntSingleDataEntity extends common
and your method is:
[SysODataAction("processPilog", false)]
The OData URL to call the action will be:
POST https:///data/ANSARIPilogFinishedGoodsIntSingleDataEntity/processPilog
Replace with your environment URL, e.g.:
https://mycompany.operations.dynamics.com/data/ANSARIPilogFinishedGoodsIntSingleDataEntity/processPilog
POST method is used because OData actions are executed with POST.
In Postman, add the following headers:
| Key | Value |
|---|---|
| Authorization | Basic or OAuth token |
| Content-Type | application/json |
| Accept | application/json |
Note: In D365 F&O, authentication can be OAuth2 (Azure AD app) or Basic auth (username/password for testing).
Set Postman to raw JSON and use POST body like this:
{ "ITEM": "FG001", "PRODUCTNAME": "Product A", "SITEID": "NY", "WAREHOUSEID": "WH1", "PRODUCTNUMBER": "P123", "PRODUCTGROUPID": "Group1", "PURCHASEUNITSYMBOL": "PCS", "SALESUNITSYMBOL": "PCS", "NETPRODUCTWEIGHT": 2.5, "PRODUCTVOLUME": 1.2, "LANGUAGE": "en-us", "DATAAREAID": "USMF" }
Each key matches your method parameters.
Make sure the data types match: strings for IDs/names, numbers for weight/volume.
Click Send in Postman.
If everything is correct, you should see a response like:
{ "return": "Processed successfully" } or { "value": "SUCCESS: Item FG-1001 created in USMF" }
This string comes from your manager.process(...) method in the class.
| Error | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Wrong auth | Check OAuth token or username/password |
| 404 Not Found | Wrong URL | Check class name & environment URL |
| 400 Bad Request | JSON mismatch | Make sure body keys exactly match method parameters |
| 500 Server Error | Logic issue | Check ANSARIPilogFinishedGoodsManager.process method |
✅ Summary Flow in Postman
URL: POST /data/ANSARIPilogFinishedGoodsIntSingleDataEntity/processPilog
Headers: Authorization, Content-Type, Accept
Body: JSON with all parameters
Send → Get the string result
First read the answer fully, then try to explain it in your own words. After that, open a few related questions and compare the concepts. This method helps you remember the topic for a longer time and improves exam preparation.