D365 Ce Bc Integration

Production engineering guide for d365 ce bc integration covering patterns, implementation strategies, and operational best practices.

Dynamics 365 Customer Engagement (D365 CE) and Business Central (BC) integrate through a combination of standard web services, data synchronization mechanisms, and event-driven workflows. This integration is essential when sales, service, and marketing teams in D365 CE require real-time access to financial, inventory, and supply chain data from BC, or when BC needs to reflect customer activities, opportunities, and service cases from CE. It matters during customer onboarding, order fulfillment, and financial close when data must flow seamlessly between the two systems, particularly in organisations that maintain CE for customer-facing operations and BC for back-office processes.

Synchronizing Customer and Account Data

Customer and account data must remain consistent across D365 CE and Business Central. The integration begins with a bidirectional sync of the Account and Contact entities.

Initial Sync via REST API

To populate BC with existing CE accounts, a scheduled job invokes a REST endpoint:

GET /api/d365ce/v1.0/Accounts?$select=AccountId,Name,AccountNumber,Address1_City,Address1_Country,Phone,Email
Authorization: Bearer <JWT_TOKEN>
Accept: application/json

The response is a JSON array of accounts. A corresponding service in BC uses this data to either create or update Customer records in the Customer table.

BC-to-CE Updates via Web Service

When a new customer is created in BC, a POST request to CE’s Web API updates the Account entity:

POST /api/d365ce/v1.0/Accounts
Content-Type: application/json
Authorization: Bearer <JWT_TOKEN>

{
  "AccountId": "00000000-0000-0000-0000-000000000000",
  "Name": "Acme Corp",
  "AccountNumber": "ACM001",
  "Address1_City": "Seattle",
  "Address1_Country": "USA",
  "Phone": "+1 206 555 0199",
  "Email": "[email protected]"
}

A successful response returns a 201 Created status and the new AccountId. A 400 Bad Request indicates missing or invalid fields, with a body like:

{
  "error": {
    "code": "400",
    "message": "The request is invalid.",
    "details": [
      {
        "message": "The 'AccountNumber' field is required.",
        "target": "AccountNumber"
      },
      {
        "message": "The 'Name' field must be at least 2 characters long.",
        "target": "Name"
      }
    ]
  }
}

Handling Duplicates and Conflicts

When an account is updated in both systems, a conflict arises. A common resolution is to track a LastSynced timestamp in each record and use it to resolve updates.

In BC, the Customer table includes a CE_ExternalId field (type Guid) that stores the AccountId from CE. When syncing, the integration logic checks whether CE_ExternalId exists. If yes, it updates the existing record; if not, it creates a new one.

When conflicts occur, the system logs a SyncConflict record with fields: SourceSystem, RecordType, ExternalId, LocalVersion, RemoteVersion, ConflictTimestamp, and ResolutionAction.

Synchronizing Sales and Service Data

Sales and service data must flow from CE to BC in real time to support order processing, inventory allocation, and service scheduling.

Creating Sales Orders in BC from CE

When a sales order is confirmed in CE, the system triggers a POST to BC’s OData endpoint:

POST /api/v2.0/Orders
Content-Type: application/json
Authorization: Bearer <BC_JWT_TOKEN>

{
  "id": "SO-2024-0001",
  "customerId": "ACM001",
  "orderDate": "2024-05-15",
  "currencyCode": "USD",
  "lines": [
    {
      "itemId": "PROD-001",
      "quantity": 10,
      "unitPrice": 25.00
    },
    {
      "itemId": "PROD-002",
      "quantity": 5,
      "unitPrice": 75.00
    }
  ]
}

BC responds with a 201 Created and the generated OrderId. If the itemId does not exist in the Item table, BC returns a 404 Not Found with:

{
  "error": {
    "code": "404",
    "message": "The item with ID 'PROD-001' was not found."
  }
}

Order Confirmation and Inventory Reservation

After creating the order in BC, the system must confirm it and reserve inventory. The integration uses a two-step process:

  1. Confirm Order:
   PATCH /api/v2.0/Orders('SO-2024-0001')
   Content-Type: application/json

   {
     "status": "Confirmed"
   }
  1. Reserve Inventory:
   POST /api/v2.0/InventoryReservations
   Content-Type: application/json

   {
     "orderId": "SO-2024-0001",
     "lineId": 1,
     "itemId": "PROD-001",
     "quantity": 10,
     "reservationDate": "2024-05-16"
   }

If reservation fails (e.g., insufficient stock), the error is logged and a 422 Unprocessable Entity response includes:

{
  "error": {
    "code": "422",
    "message": "Insufficient inventory for item 'PROD-001'.",
    "details": [
      {
        "message": "Available: 8, Requested: 10",
        "target": "Quantity"
      }
    ]
  }
}

Service Case and Work Order Synchronization

When a service case is created in CE, the system generates a corresponding work order in BC. The integration uses a POST to BC’s WorkOrders endpoint:

POST /api/v2.0/WorkOrders
Content-Type: application/json

{
  "caseId": "SC-2024-001",
  "customerId": "ACM001",
  "description": "Server downtime at client site.",
  "priority": "High",
  "assignedTo": "Tech-001",
  "startDate": "2024-05-18"
}

The WorkOrder entity in BC includes a CE_CaseId field to track the original CE case. When a work order is completed in BC, the integration sends a PUT request to CE’s ServiceCases API:

PUT /api/d365ce/v1.0/ServiceCases(SC-2024-001)
Content-Type: application/json

{
  "status": "Completed",
  "actualDuration": "PT4H30M",
  "completionDate": "2024-05-20T12:00:00Z"
}

Event-Driven Workflows and Error Handling

Integration relies on event-driven workflows. When an entity is created, updated, or deleted in one system, a message is published to a message broker.

Message Queue and Processing

A common pattern uses a message queue such as RabbitMQ or Azure Service Bus. When a customer is updated in BC, a JSON message is published to a customer-updated queue:

{
  "eventType": "CustomerUpdated",
  "timestamp": "2024-05-15T10:30:00Z",
  "data": {
    "customerId": "ACM001",
    "name": "Acme Corp",
    "lastModified": "2024-05-15T10:25:00Z",
    "externalId": "00000000-0000-0000-0000-000000000000"
  }
}

A consumer service in CE listens to this queue and updates the corresponding Account entity via the Web API.

Handling Failed Integrations

Integration failures are logged in a IntegrationLog table with the following fields: Id, SourceSystem, TargetSystem, Operation, Payload, StatusCode, ErrorMessage, RetryCount, ProcessedAt, and FailedAt.

A common failure is a transient network error during a POST to BC. The integration service implements exponential backoff with jitter:

If all retries fail, the log entry includes:

{
  "sourceSystem": "D365 CE",
  "targetSystem": "Business Central",
  "operation": "CreateSalesOrder",
  "payload": {
    "id": "SO-2024-0002",
    "customerId": "ACM002",
    "lines": [ ... ]
  },
  "statusCode": 503,
  "errorMessage": "Service Unavailable: Could not connect to BC instance at https://bc.company.com.",
  "retryCount": 4,
  "failedAt": "2024-05-15T11:05:42Z",
  "details": {
    "connectionTimeout": 30000,
    "readTimeout": 60000,
    "serverName": "bc.company.com",
    "serverPort": 443
  }
}

Silent Failures and Idempotency

The integration assumes idempotency: multiple identical requests produce the same outcome. However, a silent failure occurs when a request completes on the server but the client receives no response.

For example, a POST to create a customer in BC succeeds internally but fails to send the 201 Created response due to a network timeout. The CE system logs the request but waits for a response. After 30 seconds, it times out and retries the same request.

The BC service must handle this by checking for existing customers with the same AccountNumber before creating a new one. If a customer with AccountNumber = 'ACM001' already exists, it returns 200 OK with the existing CustomerId instead of 201 Created.

This prevents duplicate customers and ensures consistency across systems.

Configuration and Monitoring

Integration is controlled through a configuration table IntegrationSettings with the following fields:

KeyValue
CE_BaseUrlhttps://d365ce.company.com
BC_BaseUrlhttps://bc.company.com
SyncIntervalMinutes15
EnableEventDriventrue
MaxRetries4
JitterMaxMs5000
LogIntegrationErrorstrue

The integration engine reads these settings at startup and during runtime.

Monitoring Integration Health

A health check endpoint /health returns:

{
  "status": "healthy",
  "checks": [
    {
      "name": "CE_Connection",
      "status": "ok",
      "details": {
        "lastTested": "2024-05-15T10:45:00Z",
        "responseTimeMs": 124
      }
    },
    {
      "name": "BC_Connection",
      "status": "warning",
      "details": {
        "lastTested": "2024-05-15T10:44:58Z",
        "responseTimeMs": 891,
        "error": "401 Unauthorized"
      }
    },
    {
      "name": "Sync_Job",
      "status": "ok",
      "details": {
        "lastRun": "2024-05-15T10:40:00Z",
        "recordsProcessed": 42,
        "errors": 1
      }
    }
  ]
}

A status of warning or critical triggers alerts via email or Slack.

Common Pitfalls and Sharp Edges

Summary

D365 CE and Business Central integration is a distributed system of REST calls, event-driven messages, and idempotent data flows. It succeeds when data remains consistent across systems, but fails silently when timeouts, duplicates, or missing fields go unnoticed. The integration is configured through a standard key-value store, monitored via health checks, and resilient to network issues through retry strategies and idempotency. The sharp edges lie in timezone handling, partial updates, and conflict resolution—where the real-world complexity reveals itself.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.