Import
ERP.net Domain API defines an Import endpoint which can be used to import multiple entities at once.
Import is unbound (not bound to any entity) action (actions are called with HTTP POST method) that inserts, updates or deletes multiple objects. Specification with example:
{
"transaction": "all-objects" | "per-object" (default),
"model": "frontend" (default) | "backend",
"loggingLevel": 0 | 1 | 2 (default),
"objects":
[
{
"@odata.type": "Crm_Sales_Customers",
"@erpnet.action": "merge" (default) | "create" | "update" | "delete"
...
},
...
]
}
Parameters
- model: - allowed values are
commonorfrontend. This parameter indicates the data model used for the import. Front-end data model uses front-end business rules. For example front-end logic is when Quantity of a SalesOrderLine is changed the corresponding QuantityBase is calculated by a dedicated front-end business rule. Common model defines minimal business logic applicable in all cases - front-end or back-end. The default isfrontend. - transaction: -
all-objectsorper-object. This parameter defines when the changes will be commited to the database. Ifall-objectsis specified all changes are committed at once at the end of the import. Ifper-objectis specified every object is saved when it is ready. The default isper-object. - objects - an array of entity objects for import.
- loggingLevel - The logging level of the import. 0 - Does not log the operation; 1 - Creates Systems.Exchange.DataExchanges object with details about the import operation, but without listing specific objects; 2 - Also creates DataExchangeObjects for all aggregate root objects.
Properties of the objects element
- "@odata.type" - Each object must specify valid entity type. The entity type is the singular form of the entity set and can be found in the documentation for each entity. The @odata.type always starts with the default namespace
Erp.- Example Erp.General_Products_Product - "@erpnet.action" - This is an optional annotation for the desired import action. For top-level objects the default action is
create. For more information see this article. - "@erpnet.findBy" - This is an optional annotation that specifies the search criteria for the find action. For more information see this article.
- Any data property of the imported object.
Logging levels
The loggingLevel parameter controls whether the import operation is logged in the exchange subsystem and how much detail is recorded.
Supported values are 0, 1, and 2. The default value is 2.
| Level | Name | Behavior |
|---|---|---|
0 |
No logging | Does not log the import operation. |
1 |
Minimal logging | Creates a Systems.Exchange.DataExchanges record with details about the import operation, but without listing specific imported objects. |
2 |
Normal logging | Creates a Systems.Exchange.DataExchanges record and Systems.Exchange.DataExchangeObjects records for all root objects. |
Return value
Specification with example
{
"result": "success" | "fail",
"objects":
[
{
"@erpnet.result": "success" | "fail",
"@odata.id": "Crm_Sales_Customers(<guid>)",
"@erpnet.message": "<error-message>"
"@erpnet.state": "Added" | "Modified" | "Deleted" | "Unchanged"
},
...
],
"data-exchange": "Systems_Exchange_DataExchanges(<guid>)"
}
Properties of the result value
- "@erpnet.result" -
successorfail. The result issuccessonly if all objects are imported successfully. - objects - an array of object results - one object for each imported object.
Properies of the each returned object
- "@erpnet.result" -
successorfail. - "@odata.id" - the ODATA Id of the imported object. If result is
failthis is not available. - "@erpnet.message" - the error message.
- "@erpnet.state" - the status of the imported object. One of "Added" | "Modified" | "Deleted" | "Unchanged". Indicates the operation performed for the object. Only the "@odata.id" is included in the result - no other properties.
- "data-exchange" - the odata id of the created DataExchange object. Not present if the loggingLevel is 0.
Visual Import Tool
The Domain API site also provides a Visual Import Tool at the /api/domain/import endpoint.
Try it here: https://testdb.my.erp.net/api/domain/import
The tool allows you to work with import payloads interactively in the browser. You can type, paste, edit, and execute import JSON directly in the page.
The tool includes a text editor where you can type, paste, edit, and execute import JSON.
It supports full code completion and validation for the import payload.
The tool also supports uploading import input from files. Supported file formats are:
- text files -
.txt,.json .zipfiles
This is useful when you want to compose import payloads visually, validate their structure with schema-based assistance, or test imports without sending requests programmatically.
Examples
Import Products
The following example performs merge action for General_Products_Products.
If existing product is found by the provided ExternalId it's PartNumber, BaseMeasurementCategory, MeasurementUnit, Name and ProductGroup are updated.
The action for the referenced objects is find because the included properties are only these that can be used in find criteria. BaseMeasurementCategory is searched by Name (providing @erpnet.findBy), MeasurementUnit and ProductGroup are searched by Code.
POST ~/Import
{
"model": "frontend",
"transaction": "per-object",
"objects": [
{
"@odata.type": "Erp.General_Products_Product",
"@erpnet.action": "merge",
"ExternalId": "EXT000",
"PartNumber": "DATP000",
"BaseMeasurementCategory": {
"@erpnet.action": "find",
"@erpnet.findBy": {
"Name": "Pieces"
}
},
"MeasurementUnit": {
"Code": "pcs"
},
"Name": {
"EN": "Domain API Test 000"
},
"ProductGroup": {
"Code": "DATG01",
"Name": {
"EN": "Domain API Tests"
}
}
},
{
"@odata.type": "Erp.General_Products_Product",
"@erpnet.action": "merge",
"ExternalId": "EXT001",
"PartNumber": "DATP001",
"BaseMeasurementCategory": {
"@erpnet.action": "find",
"@erpnet.findBy": {
"Name": "Pieces"
}
},
"MeasurementUnit": {
"Code": "pcs"
},
"Name": {
"EN": "Domain API Test 001"
},
"ProductGroup": {
"Code": "DATG01",
"Name": {
"EN": "Domain API Tests"
}
}
},
{
"@odata.type": "Erp.General_Products_Product",
"@erpnet.action": "merge",
"ExternalId": "EXT002",
"PartNumber": "DATP002",
"BaseMeasurementCategory": {
"@erpnet.action": "find",
"@erpnet.findBy": {
"Name": "Pieces"
}
},
"MeasurementUnit": {
"Code": "pcs"
},
"Name": {
"EN": "Domain API Test 002"
},
"ProductGroup": {
"Code": "DATG01",
"Name": {
"EN": "Domain API Tests"
}
}
}
]
}
Import Sales Order
In this example, the Import action demonstrates how you can create or update complex records without manually specifying IDs.
The key convenience is that referenced objects (like Customer, Product, or ProductGroup) can be automatically imported or updated within the same request.
This makes the Import API extremely useful for data synchronization with external systems, where you may not have direct access to internal record IDs but still need to ensure all related entities are properly linked and up to date.
The system automatically determines the @erpnet.action and @erpnet.findBy criteria based on the provided properties of the nested objects.
POST https://testdb.my.erp.net/api/domain/odata/Import
{
"objects":[
{
"@odata.type":"Erp.Crm_Sales_SalesOrder",
"DocumentType":{
"Code":"CRM_SALES_ORDER"
},
"EnterpriseCompany":{
"@erpnet.findBy":{
"Code":"714895"
}
},
"EnterpriseCompanyLocation":{
"PartyCode":"00111"
},
"Customer":{
"Number":"CST001",
"Party":{
"@odata.type":"Erp.General_Contacts_Company",
"PartyCode":"CST001",
"Name":{
"EN":"Customer 01"
},
"RegistrationType":{
"EN":"Ltd"
},
"RegistrationNumber":"001001001"
},
"EnterpriseCompany":{
"@erpnet.findBy":{
"Code":"714895"
}
}
},
"DocumentCurrency":{
"CurrencySign":"GBP"
},
"Lines":[
{
"Product":{
"PartNumber":"PRD001",
"BaseMeasurementCategory":{
"@erpnet.action":"find",
"@erpnet.findBy":{
"Name":"Pieces"
}
},
"MeasurementUnit":{
"Code":"pcs"
},
"Name":{
"EN":"Product 001"
},
"ProductGroup":{
"Code":"PGT01",
"Name":{
"EN":"Product Group 01"
}
}
},
"QuantityUnit":{
"Code":"pcs"
},
"Quantity":{
"Value":1,
"Unit":"pcs"
},
"UnitPrice":{
"Value":20,
"Currency":"GBP"
}
}
]
}
]
}
NOTE In case
@erpnet.actionannotation is missing, a default value is used. For top-level objects the default value of@erpnet.actioniscreate. For nested objects the default@erpnet.actionisfindif only properties defining the search criteria are provided (either @erpnet.findBy or data properties usable in a find action). If other data properties are provided the default@erpnet.actionismerge. See @erpnet.action topic.
Explanation of the Automatic Actions
| Property | Automatic Action | Description |
|---|---|---|
| DocumentType | find |
The system searches for a document type with Code = "CRM_SALES_ORDER". Document types are predefined, so we search by code. |
| EnterpriseCompany | find |
Since only Code is provided, the system performs a lookup for an existing Enterprise Company with that code. |
| EnterpriseCompanyLocation | find |
The field PartyCode uniquely identifies the company location, so the system searches by it. |
| Customer | merge |
Customers are matched by Number. If a customer with Number = "CST001" exists, it’s passed to the sales order. Otherwise, a new one is created. The Party subobject (Company) will also be created if missing. |
| Customer → Party | merge |
Because Party is an abstract class, the type is explicitly specified as Erp.General_Contacts_Company. A new Party (Company) record is created if none exists (The system first looks up existing party by PartyCode). |
| DocumentCurrency | find |
The CurrencySign uniquely identifies the currency (e.g., "GBP"), so an existing record is used. |
| Lines | — | Represents an array of line items that will be created as part of the sales order. |
| Product | merge |
The system searches for an existing product with PartNumber = "PRD001". If not found, a new product is created. This behavior prevents duplicate product definitions. |
| Product → BaseMeasurementCategory | find |
Uses Name = "Pieces" to locate the measurement category. Note that searching by multi-language properties is always with contains criteria. |
| Product → MeasurementUnit | find |
Looks up the measurement unit by Code = "pcs". |
| Product → ProductGroup | merge |
Uses Code = "PGT01" to find or create a product group. If it exists, it’s reused; otherwise, it’s created with the given name. |
| QuantityUnit | find |
The system searches for a measurement unit with Code = "pcs". |
| Quantity / UnitPrice | — | These are complex properties of the order line and are directly assigned, not looked up. |
Error Handling
The result contains the error message for each failed object.
Example:
{
"@erpnet.result": "fail",
"objects": [
{
"@erpnet.result": "fail",
"@erpnet.message": "Object not found: EnterpriseCompany, action: find, findBy: {\"Code\":\"546346373\"}.",
"@erpnet.error": {
"message": "Object not found: EnterpriseCompany, action: find, findBy: {\"Code\":\"546346373\"}.",
"code": 0,
"type": "InvalidOperationException",
"info": "System.InvalidOperationException: Object not found: EnterpriseCompany, action: find, findBy: {\"Code\":\"546346373\"}.\r\n at ErpNet.Model.OData.ODataResourceInfo.GetObject()\r\n at ErpNet.Model.OData.ODataResourceInfo.Execute()\r\n at ErpNet.Model.OData.ODataResourceInfo.Execute()\r\n at ErpNet.Model.OData.ODataResourceInfo.Execute()\r\n at ErpNet.Model.OData.Operations.ImportAction.HandleRequest(ODataContext odataContext, IDictionary`2 parameters, IODataRequestMessage requestMessage)",
"messageFormat": null,
"parameters": null
}
}
]
}