Creating and Using Custom Services in D365FO

 Custom services allow you to expose X++ business logic as web services that external systems can consume. These services are defined using X++ classes and methods, and they are published through service groups in D365FO.


You can use custom services when:

  • You need to perform complex business logic not possible through data entities.                Ex:Calculate Custom Discount and Tax Based on Business Rules

  • You want to control how data is processed or returned.

  • You are building a lightweight API for integration purposes.


🧱 Types of Services in D365FO

Before diving into custom services, it’s good to know the types of services available in D365FO:

TypePurpose
ODataStandard way to expose data entities for CRUD operations
Custom ServicesX++ classes exposed as SOAP/REST endpoints
DMF / Data EntitiesUsed for data import/export (also accessible via OData)
Business EventsFor pushing notifications to external systems

In this blog, we focus on custom services, which give you the flexibility to expose logic-driven endpoints.



To build a custom service in D365FO, you typically need three classes

Request Contract : Defines input parameters (from external system)

Response Contract : Defines what you want to return

Service Class : Contains the business logic


 Request Class: CustomerDiscountRequestContract

This class receives input from the caller, like customer account and sales amount.


[DataContract] public class CustomerDiscountRequestContract { str custAccount; real salesAmount; [DataMember] public str parmCustAccount(str _custAccount = custAccount) { custAccount = _custAccount; return custAccount; } [DataMember] public real parmSalesAmount(real _salesAmount = salesAmount) { salesAmount = _salesAmount; return salesAmount; } }


Response Class: CustomerDiscountResponseContract

This class defines the output returned by the service, such as the final discounted amount and a message.


[DataContract] public class CustomerDiscountResponseContract { real discountedAmount; str message; [DataMember] public real parmDiscountedAmount(real _discountedAmount = discountedAmount) { discountedAmount = _discountedAmount; return discountedAmount; } [DataMember] public str parmMessage(str _message = message) { message = _message; return message; } }

Service Class: CustomerDiscountService

This class contains the actual business logic. It accepts the request contract, performs calculations, and returns the response contract.


public class CustomerDiscountService { public CustomerDiscountResponseContract calculateDiscount(CustomerDiscountRequestContract request) { CustomerDiscountResponseContract response = new CustomerDiscountResponseContract(); CustTable custTable; real discountPercentage = 0.0; real discountedAmount; select firstOnly custTable where custTable.AccountNum == request.parmCustAccount(); if (!custTable) { response.parmMessage("Customer not found"); response.parmDiscountedAmount(0); return response; } // Apply discount based on customer group switch (custTable.CustGroup) { case '10': discountPercentage = 10; break; case '20': discountPercentage = 5; break; default: discountPercentage = 2; break; } discountedAmount = request.parmSalesAmount() * (1 - (discountPercentage / 100.0)); response.parmDiscountedAmount(discountedAmount); response.parmMessage(strFmt("Discount of %1%% applied for customer %2", discountPercentage, custTable.AccountNum)); return response; } }

Exposing the Service

After writing these classes, you need to:

  1. Create a Service Group in AOT (e.g., CustomerDiscountServiceGroup)

  2. Add the CustomerDiscountService class to the group

  3. Set AutoDeploy to Yes

  4. Deploy it (Build the solution)

Serivice can be accessed by the below url

POST https://<baseURL>/api/services/CustomerDiscountServiceGroup/CustomerDiscountService/calculateDiscount

📤 Sample Resquest JSON

{ "custAccount": "C01000", "salesAmount": 1000 }

📤 Sample Response JSON

{ "discountedAmount": 900, "message": "Discount of 10% applied for customer C01000" }

Comments