Dynamics 365 FO – Fetch Customer Details Integration API (With Full X++ Code + JSON Explanation)
In this article, I am sharing the complete implementation of my Customer Details Integration API in Microsoft Dynamics 365 Finance & Operations (D365 FO).
This API is designed to:
✔ Fetch a specific customer when CustAccount is provided in the JSON
✔ Fetch all customers when CustAccount is NOT provided
✔ Return multiple addresses and multiple contacts for each customer
✔ Return financial dimensions (BusinessUnit, CostCenter, Department, Project)
⭐ How the API Works
1. When CustAccount is provided
If the JSON contains:
{
"DataAreaId": "USMF",
"CustAccount": "1101"
}
➡ The API returns only that specific customer's details, including their addresses, contacts, and dimensions.
2. When CustAccount is NOT provided
If JSON is:
{
"DataAreaId": "USMF"
}
➡ The API returns all customers from the selected company (changecompany(DataAreaId) is used).
๐ Top-Level Response Structure
The API returns:
-
SuccessMessage
-
ErrorMessage
-
DebugMessage
-
List of Customers
-
Each customer contains:
-
Account, Name, Group, Currency
-
Dimensions
-
List of Addresses
-
List of Contacts
๐งพ Main Response Contract Class
[DataContractAttribute]
class MZNFRCustomerMainResponse
{
private List customers;
private boolean successMessage;
private str errorMessage;
private str debugMessage;
[DataMemberAttribute('Customers'),
AifCollectionTypeAttribute("Customers",Types::class,classstr(MZNFRCustomerResponse)),
AifCollectionTypeAttribute("return",types::class,classstr(MZNFRCustomerResponse))]
public List parmCustomers(List _customers = customers)
{
if(!prmIsDefault(_customers))
{
customers = _customers;
}
return customers;
}
[DataMember("SuccessMessage")]
public boolean parmSuccessMessage(boolean _successMessage = successMessage)
{
if(!prmIsDefault(_successMessage))
{
successMessage = _successMessage;
}
return successMessage;
}
[DataMember("ErrorMessage")]
public str parmErrorMessage(str _errorMessage = errorMessage)
{
if(!prmIsDefault(_errorMessage))
{
errorMessage = _errorMessage;
}
return errorMessage;
}
[DataMember("DebugMessage")]
public str parmDebugMessage(str _debugMessage = debugMessage)
{
if(!prmIsDefault(_debugMessage))
{
debugMessage = _debugMessage;
}
return debugMessage;
}
}
๐จ Customer Request Class (Input from JSON)
[DataContractAttribute]
class MZNFRCustomerRequest
{
private DataAreaId dataAreaId;
private AccountNum custAccount;
[DataMember("DataAreaId")]
public DataAreaId parmDataAreaId(DataAreaId _dataAreaId = dataAreaId)
{
if (!prmIsDefault(_dataAreaId))
{
dataAreaId = _dataAreaId;
}
return dataAreaId;
}
[DataMember("CustAccount")]
public AccountNum parmCustAccount(AccountNum _custAccount = custAccount)
{
if(!prmIsDefault(_custAccount))
{
custAccount = _custAccount;
}
return custAccount;
}
}
๐ Customer Address Response Class
[DataContractAttribute]
class MZNFRCustomerAddressReponse
{
private str region;
private str postalCode;
private str street;
private str city;
private str state;
private str country;
private boolean isPrimary;
[DataMember("Region")]
public str parmRegion(str _region = region)
{
if(!prmIsDefault(_region))
{
region = _region;
}
return region;
}
[DataMember("PostalCode")]
public str parmPostalCode(str _postalCode = postalCode)
{
if(!prmIsDefault(_postalCode))
{
postalCode = _postalCode;
}
return postalCode;
}
[DataMember("Street")]
public str parmStreet(str _street = street)
{
if(!prmIsDefault(_street))
{
street = _street;
}
return street;
}
[DataMember("City")]
public str parmCity(str _city = city)
{
if(!prmIsDefault(_city))
{
city = _city;
}
return city;
}
[DataMember("State")]
public str parmState(str _state = state)
{
if(!prmIsDefault(_state))
{
state = _state;
}
return state;
}
[DataMember("Country")]
public str parmCountry(str _country = country)
{
if(!prmIsDefault(_country))
{
country = country;
}
return country;
}
[DataMember("Primary")]
public boolean parmIsPrimary(boolean _isPrimary = isPrimary)
{
if(!prmIsDefault(_isPrimary))
{
isPrimary = _isPrimary;
}
return isPrimary;
}
}
☎ Customer Contacts Response Class
[DataContractAttribute]
class MZNFRCustomerContactsReponse
{
private str type;
private str description;
private str contactNumber;
boolean isPrimary;
[DataMember("Type")]
public str parmType(str _type = type)
{
if(!prmIsDefault(_type))
{
type = _type;
}
return type;
}
[DataMember("Description")]
public str parmDescription(str _description = description)
{
if(!prmIsDefault(_description))
{
description = _description;
}
return description;
}
[DataMember("ContactNumber")]
public str parmContactNumber(str _contactNumber = contactNumber)
{
if(!prmIsDefault(_contactNumber))
{
contactNumber = _contactNumber;
}
return contactNumber;
}
[DataMember("Primary")]
public boolean parmIsPrimary(boolean _isPrimary = isPrimary)
{
if(!prmIsDefault(_isPrimary))
{
isPrimary = _isPrimary;
}
return isPrimary;
}
}
๐ฏ Customer Response Class (Full Customer Object)
[DataContractAttribute]
class MZNFRCustomerResponse
{
private CustAccount custAccount;
private Name custName;
private CustGroupId custGroupId;
private CurrencyCode currency;
private str businessUnit;
private str costCenter;
private str Department;
private str Project;
private List addresses;
private List contacts;
[DataMember("CustAccount")]
public CustAccount parmCustAccount(CustAccount _custAccount = custAccount)
{
if(!prmIsDefault(_custAccount))
{
custAccount = _custAccount;
}
return custAccount;
}
[DataMember("CustName")]
public Name parmCustName(Name _custName = custName)
{
if(!prmIsDefault(_custName))
{
custName = _custName;
}
return custName;
}
[DataMember("CustGroupId")]
public Name parmCustGroupId(Name _custGroupId = custGroupId)
{
if(!prmIsDefault(_custGroupId))
{
custGroupId = _custGroupId;
}
return custGroupId;
}
[DataMember("Currency")]
public CurrencyCode parmCurrency(CurrencyCode _currency = currency)
{
if(!prmIsDefault(_currency))
{
currency = _currency;
}
return currency;
}
[DataMember("BusinessUnit")]
public str parmBusinessUnit(str _businessUnit = businessUnit)
{
if(!prmIsDefault(_businessUnit))
{
businessUnit = _businessUnit;
}
return businessUnit;
}
[DataMember("CostCenter")]
public str parmCostCenter(str _costCenter = costCenter)
{
if(!prmIsDefault(_costCenter))
{
costCenter = _costCenter;
}
return costCenter;
}
[DataMember("Department")]
public str parmDepartment(str _department = department)
{
if(!prmIsDefault(_department))
{
department = _department;
}
return department;
}
[DataMember("Project")]
public str parmProject(str _Project = Project)
{
if(!prmIsDefault(_Project))
{
Project = _Project;
}
return Project;
}
[DataMemberAttribute('Addresses'),
AifCollectionTypeAttribute("Addresses",Types::class,classstr(MZNFRCustomerAddressReponse)),
AifCollectionTypeAttribute("return",types::class,classstr(MZNFRCustomerAddressReponse))]
public List parmAddresses(List _addresses = addresses)
{
if(!prmIsDefault(_addresses))
{
addresses = _addresses;
}
return addresses;
}
[DataMemberAttribute('Contacts'),
AifCollectionTypeAttribute("Contacts",Types::class,classstr(MZNFRCustomerContactsReponse)),
AifCollectionTypeAttribute("return",types::class,classstr(MZNFRCustomerContactsReponse))]
public List parmContacts(List _contacts = contacts)
{
if(!prmIsDefault(_contacts))
{
contacts = _contacts;
}
return contacts;
}
}
๐ง Main API Service Class
class MZNFRCustomerService
{
public MZNFRCustomerMainResponse getCustomerDetails(MZNFRCustomerRequest _request)
{
MZNFRCustomerMainResponse customerMainResponse = new MZNFRCustomerMainResponse();
List customerList = new List(Types::Class);
changecompany(_request.parmDataAreaId())
{
try
{
CustTable custTable;
Query query = new Query();
QueryRun queryRun;
QueryBuildDataSource qbds;
QueryBuildRange qbr;
qbds = query.addDataSource(tableNum(CustTable));
if (_request.parmCustAccount())
{
qbr = qbds.addRange(fieldNum(CustTable, AccountNum));
qbr.value(_request.parmCustAccount());
}
queryRun = new QueryRun(query);
while(queryRun.next())
{
MZNFRCustomerResponse response = new MZNFRCustomerResponse();
custTable = queryRun.get(tableNum(CustTable));
response.parmCustAccount(custTable.AccountNum);
response.parmCustName(custTable.name());
response.parmCustGroupId(custTable.CustGroup);
response.parmCurrency(custTable.Currency);
response.parmBusinessUnit(this.getDimensionDisplayValue(custTable.DefaultDimension,"BusinessUnit"));
response.parmCostCenter(this.getDimensionDisplayValue(custTable.DefaultDimension,"CostCenter"));
response.parmDepartment(this.getDimensionDisplayValue(custTable.DefaultDimension,"Department"));
response.parmProject(this.getDimensionDisplayValue(custTable.DefaultDimension,"Project"));
CustTable custTableAddress;
DirPartyLocation dirPartyLocation;
LogisticsLocation logisticsLocation;
LogisticsPostalAddress logisticsPostalAddress;
LogisticsElectronicAddress logisticsElectronicAddress;
List customerAddressList = new List(Types::Class);
while select custTableAddress
where custTableAddress.AccountNum == custTable.AccountNum
join dirPartyLocation
where dirPartyLocation.Party == custTableAddress.Party
join logisticsLocation
where logisticsLocation.RecId == dirPartyLocation.Location
join logisticsPostalAddress
where logisticsPostalAddress.Location == logisticsLocation.RecId
{
MZNFRCustomerAddressReponse addressReponse = new MZNFRCustomerAddressReponse();
addressReponse.parmRegion(logisticsPostalAddress.CountryRegionId);
addressReponse.parmPostalCode(logisticsPostalAddress.ZipCode);
addressReponse.parmStreet(logisticsPostalAddress.Street);
addressReponse.parmCity(logisticsPostalAddress.City);
addressReponse.parmState(logisticsPostalAddress.State);
addressReponse.parmCountry(logisticsPostalAddress.County);
addressReponse.parmIsPrimary(dirPartyLocation.IsPrimary);
customerAddressList.addEnd(addressReponse);
}
response.parmAddresses(customerAddressList);
List customerContactsList = new List(Types::Class);
logisticsElectronicAddress.clear();
dirPartyLocation.clear();
while select * from logisticsElectronicAddress
join dirPartyLocation
where logisticsElectronicAddress.Location == dirPartyLocation.Location
&& dirPartyLocation.Party == custTable.Party
{
MZNFRCustomerContactsReponse contactsReponse = new MZNFRCustomerContactsReponse();
contactsReponse.parmType(enum2Str(logisticsElectronicAddress.Type));
contactsReponse.parmDescription(logisticsElectronicAddress.Description);
contactsReponse.parmContactNumber(logisticsElectronicAddress.Locator);
contactsReponse.parmIsPrimary(logisticsElectronicAddress.IsPrimary);
customerContactsList.addEnd(contactsReponse);
}
response.parmContacts(customerContactsList);
customerList.addEnd(response);
}
customerMainResponse.parmCustomers(customerList);
customerMainResponse.parmSuccessMessage(true);
customerMainResponse.parmDebugMessage("Customer details fetched successfully");
}
catch (Exception::CLRError)
{
System.Exception interopException = CLRInterop::getLastException();
customerMainResponse.parmSuccessMessage(false);
customerMainResponse.parmErrorMessage(interopException.ToString());
}
}
return customerMainResponse;
}
public str getDimensionDisplayValue(RecId defaultDimension, Name _dimensionName)
{
DefaultDimensionView DefaultDimensionView;
select firstonly1 DefaultDimensionView
where DefaultDimensionView.Name == _dimensionName
&& DefaultDimensionView.DefaultDimension == defaultDimension;
str value = DefaultDimensionView.dimensionDiscription();
return DefaultDimensionView.dimensionDiscription();
}
}
๐ Conclusion
This API is flexible, fast, and extremely useful for integrating external systems with Dynamics 365 Finance & Operations.
It supports:
✔ Single customer fetch
✔ Bulk customer fetch
✔ Multi-address response
✔ Multi-contact response
✔ Full dimensions mapping